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     var onStop = function(e){
3044         dragEl = null;
3045         clearProc();
3046     };
3047     
3048     var triggerRefresh = function(){
3049         if(ddm.dragCurrent){
3050              ddm.refreshCache(ddm.dragCurrent.groups);
3051         }
3052     };
3053     
3054     var doScroll = function(){
3055         if(ddm.dragCurrent){
3056             var dds = Roo.dd.ScrollManager;
3057             if(!dds.animate){
3058                 if(proc.el.scroll(proc.dir, dds.increment)){
3059                     triggerRefresh();
3060                 }
3061             }else{
3062                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
3063             }
3064         }
3065     };
3066     
3067     var clearProc = function(){
3068         if(proc.id){
3069             clearInterval(proc.id);
3070         }
3071         proc.id = 0;
3072         proc.el = null;
3073         proc.dir = "";
3074     };
3075     
3076     var startProc = function(el, dir){
3077         clearProc();
3078         proc.el = el;
3079         proc.dir = dir;
3080         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
3081     };
3082     
3083     var onFire = function(e, isDrop){
3084         if(isDrop || !ddm.dragCurrent){ return; }
3085         var dds = Roo.dd.ScrollManager;
3086         if(!dragEl || dragEl != ddm.dragCurrent){
3087             dragEl = ddm.dragCurrent;
3088             // refresh regions on drag start
3089             dds.refreshCache();
3090         }
3091         
3092         var xy = Roo.lib.Event.getXY(e);
3093         var pt = new Roo.lib.Point(xy[0], xy[1]);
3094         for(var id in els){
3095             var el = els[id], r = el._region;
3096             if(r && r.contains(pt) && el.isScrollable()){
3097                 if(r.bottom - pt.y <= dds.thresh){
3098                     if(proc.el != el){
3099                         startProc(el, "down");
3100                     }
3101                     return;
3102                 }else if(r.right - pt.x <= dds.thresh){
3103                     if(proc.el != el){
3104                         startProc(el, "left");
3105                     }
3106                     return;
3107                 }else if(pt.y - r.top <= dds.thresh){
3108                     if(proc.el != el){
3109                         startProc(el, "up");
3110                     }
3111                     return;
3112                 }else if(pt.x - r.left <= dds.thresh){
3113                     if(proc.el != el){
3114                         startProc(el, "right");
3115                     }
3116                     return;
3117                 }
3118             }
3119         }
3120         clearProc();
3121     };
3122     
3123     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
3124     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
3125     
3126     return {
3127         /**
3128          * Registers new overflow element(s) to auto scroll
3129          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
3130          */
3131         register : function(el){
3132             if(el instanceof Array){
3133                 for(var i = 0, len = el.length; i < len; i++) {
3134                         this.register(el[i]);
3135                 }
3136             }else{
3137                 el = Roo.get(el);
3138                 els[el.id] = el;
3139             }
3140         },
3141         
3142         /**
3143          * Unregisters overflow element(s) so they are no longer scrolled
3144          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
3145          */
3146         unregister : function(el){
3147             if(el instanceof Array){
3148                 for(var i = 0, len = el.length; i < len; i++) {
3149                         this.unregister(el[i]);
3150                 }
3151             }else{
3152                 el = Roo.get(el);
3153                 delete els[el.id];
3154             }
3155         },
3156         
3157         /**
3158          * The number of pixels from the edge of a container the pointer needs to be to 
3159          * trigger scrolling (defaults to 25)
3160          * @type Number
3161          */
3162         thresh : 25,
3163         
3164         /**
3165          * The number of pixels to scroll in each scroll increment (defaults to 50)
3166          * @type Number
3167          */
3168         increment : 100,
3169         
3170         /**
3171          * The frequency of scrolls in milliseconds (defaults to 500)
3172          * @type Number
3173          */
3174         frequency : 500,
3175         
3176         /**
3177          * True to animate the scroll (defaults to true)
3178          * @type Boolean
3179          */
3180         animate: true,
3181         
3182         /**
3183          * The animation duration in seconds - 
3184          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
3185          * @type Number
3186          */
3187         animDuration: .4,
3188         
3189         /**
3190          * Manually trigger a cache refresh.
3191          */
3192         refreshCache : function(){
3193             for(var id in els){
3194                 if(typeof els[id] == 'object'){ // for people extending the object prototype
3195                     els[id]._region = els[id].getRegion();
3196                 }
3197             }
3198         }
3199     };
3200 }();/*
3201  * Based on:
3202  * Ext JS Library 1.1.1
3203  * Copyright(c) 2006-2007, Ext JS, LLC.
3204  *
3205  * Originally Released Under LGPL - original licence link has changed is not relivant.
3206  *
3207  * Fork - LGPL
3208  * <script type="text/javascript">
3209  */
3210  
3211
3212 /**
3213  * @class Roo.dd.Registry
3214  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
3215  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
3216  * @singleton
3217  */
3218 Roo.dd.Registry = function(){
3219     var elements = {}; 
3220     var handles = {}; 
3221     var autoIdSeed = 0;
3222
3223     var getId = function(el, autogen){
3224         if(typeof el == "string"){
3225             return el;
3226         }
3227         var id = el.id;
3228         if(!id && autogen !== false){
3229             id = "roodd-" + (++autoIdSeed);
3230             el.id = id;
3231         }
3232         return id;
3233     };
3234     
3235     return {
3236     /**
3237      * Register a drag drop element
3238      * @param {String|HTMLElement} element The id or DOM node to register
3239      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
3240      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
3241      * knows how to interpret, plus there are some specific properties known to the Registry that should be
3242      * populated in the data object (if applicable):
3243      * <pre>
3244 Value      Description<br />
3245 ---------  ------------------------------------------<br />
3246 handles    Array of DOM nodes that trigger dragging<br />
3247            for the element being registered<br />
3248 isHandle   True if the element passed in triggers<br />
3249            dragging itself, else false
3250 </pre>
3251      */
3252         register : function(el, data){
3253             data = data || {};
3254             if(typeof el == "string"){
3255                 el = document.getElementById(el);
3256             }
3257             data.ddel = el;
3258             elements[getId(el)] = data;
3259             if(data.isHandle !== false){
3260                 handles[data.ddel.id] = data;
3261             }
3262             if(data.handles){
3263                 var hs = data.handles;
3264                 for(var i = 0, len = hs.length; i < len; i++){
3265                         handles[getId(hs[i])] = data;
3266                 }
3267             }
3268         },
3269
3270     /**
3271      * Unregister a drag drop element
3272      * @param {String|HTMLElement}  element The id or DOM node to unregister
3273      */
3274         unregister : function(el){
3275             var id = getId(el, false);
3276             var data = elements[id];
3277             if(data){
3278                 delete elements[id];
3279                 if(data.handles){
3280                     var hs = data.handles;
3281                     for(var i = 0, len = hs.length; i < len; i++){
3282                         delete handles[getId(hs[i], false)];
3283                     }
3284                 }
3285             }
3286         },
3287
3288     /**
3289      * Returns the handle registered for a DOM Node by id
3290      * @param {String|HTMLElement} id The DOM node or id to look up
3291      * @return {Object} handle The custom handle data
3292      */
3293         getHandle : function(id){
3294             if(typeof id != "string"){ // must be element?
3295                 id = id.id;
3296             }
3297             return handles[id];
3298         },
3299
3300     /**
3301      * Returns the handle that is registered for the DOM node that is the target of the event
3302      * @param {Event} e The event
3303      * @return {Object} handle The custom handle data
3304      */
3305         getHandleFromEvent : function(e){
3306             var t = Roo.lib.Event.getTarget(e);
3307             return t ? handles[t.id] : null;
3308         },
3309
3310     /**
3311      * Returns a custom data object that is registered for a DOM node by id
3312      * @param {String|HTMLElement} id The DOM node or id to look up
3313      * @return {Object} data The custom data
3314      */
3315         getTarget : function(id){
3316             if(typeof id != "string"){ // must be element?
3317                 id = id.id;
3318             }
3319             return elements[id];
3320         },
3321
3322     /**
3323      * Returns a custom data object that is registered for the DOM node that is the target of the event
3324      * @param {Event} e The event
3325      * @return {Object} data The custom data
3326      */
3327         getTargetFromEvent : function(e){
3328             var t = Roo.lib.Event.getTarget(e);
3329             return t ? elements[t.id] || handles[t.id] : null;
3330         }
3331     };
3332 }();/*
3333  * Based on:
3334  * Ext JS Library 1.1.1
3335  * Copyright(c) 2006-2007, Ext JS, LLC.
3336  *
3337  * Originally Released Under LGPL - original licence link has changed is not relivant.
3338  *
3339  * Fork - LGPL
3340  * <script type="text/javascript">
3341  */
3342  
3343
3344 /**
3345  * @class Roo.dd.StatusProxy
3346  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
3347  * default drag proxy used by all Roo.dd components.
3348  * @constructor
3349  * @param {Object} config
3350  */
3351 Roo.dd.StatusProxy = function(config){
3352     Roo.apply(this, config);
3353     this.id = this.id || Roo.id();
3354     this.el = new Roo.Layer({
3355         dh: {
3356             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
3357                 {tag: "div", cls: "x-dd-drop-icon"},
3358                 {tag: "div", cls: "x-dd-drag-ghost"}
3359             ]
3360         }, 
3361         shadow: !config || config.shadow !== false
3362     });
3363     this.ghost = Roo.get(this.el.dom.childNodes[1]);
3364     this.dropStatus = this.dropNotAllowed;
3365 };
3366
3367 Roo.dd.StatusProxy.prototype = {
3368     /**
3369      * @cfg {String} dropAllowed
3370      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
3371      */
3372     dropAllowed : "x-dd-drop-ok",
3373     /**
3374      * @cfg {String} dropNotAllowed
3375      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
3376      */
3377     dropNotAllowed : "x-dd-drop-nodrop",
3378
3379     /**
3380      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
3381      * over the current target element.
3382      * @param {String} cssClass The css class for the new drop status indicator image
3383      */
3384     setStatus : function(cssClass){
3385         cssClass = cssClass || this.dropNotAllowed;
3386         if(this.dropStatus != cssClass){
3387             this.el.replaceClass(this.dropStatus, cssClass);
3388             this.dropStatus = cssClass;
3389         }
3390     },
3391
3392     /**
3393      * Resets the status indicator to the default dropNotAllowed value
3394      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
3395      */
3396     reset : function(clearGhost){
3397         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
3398         this.dropStatus = this.dropNotAllowed;
3399         if(clearGhost){
3400             this.ghost.update("");
3401         }
3402     },
3403
3404     /**
3405      * Updates the contents of the ghost element
3406      * @param {String} html The html that will replace the current innerHTML of the ghost element
3407      */
3408     update : function(html){
3409         if(typeof html == "string"){
3410             this.ghost.update(html);
3411         }else{
3412             this.ghost.update("");
3413             html.style.margin = "0";
3414             this.ghost.dom.appendChild(html);
3415         }
3416         // ensure float = none set?? cant remember why though.
3417         var el = this.ghost.dom.firstChild;
3418                 if(el){
3419                         Roo.fly(el).setStyle('float', 'none');
3420                 }
3421     },
3422     
3423     /**
3424      * Returns the underlying proxy {@link Roo.Layer}
3425      * @return {Roo.Layer} el
3426     */
3427     getEl : function(){
3428         return this.el;
3429     },
3430
3431     /**
3432      * Returns the ghost element
3433      * @return {Roo.Element} el
3434      */
3435     getGhost : function(){
3436         return this.ghost;
3437     },
3438
3439     /**
3440      * Hides the proxy
3441      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
3442      */
3443     hide : function(clear){
3444         this.el.hide();
3445         if(clear){
3446             this.reset(true);
3447         }
3448     },
3449
3450     /**
3451      * Stops the repair animation if it's currently running
3452      */
3453     stop : function(){
3454         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
3455             this.anim.stop();
3456         }
3457     },
3458
3459     /**
3460      * Displays this proxy
3461      */
3462     show : function(){
3463         this.el.show();
3464     },
3465
3466     /**
3467      * Force the Layer to sync its shadow and shim positions to the element
3468      */
3469     sync : function(){
3470         this.el.sync();
3471     },
3472
3473     /**
3474      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
3475      * invalid drop operation by the item being dragged.
3476      * @param {Array} xy The XY position of the element ([x, y])
3477      * @param {Function} callback The function to call after the repair is complete
3478      * @param {Object} scope The scope in which to execute the callback
3479      */
3480     repair : function(xy, callback, scope){
3481         this.callback = callback;
3482         this.scope = scope;
3483         if(xy && this.animRepair !== false){
3484             this.el.addClass("x-dd-drag-repair");
3485             this.el.hideUnders(true);
3486             this.anim = this.el.shift({
3487                 duration: this.repairDuration || .5,
3488                 easing: 'easeOut',
3489                 xy: xy,
3490                 stopFx: true,
3491                 callback: this.afterRepair,
3492                 scope: this
3493             });
3494         }else{
3495             this.afterRepair();
3496         }
3497     },
3498
3499     // private
3500     afterRepair : function(){
3501         this.hide(true);
3502         if(typeof this.callback == "function"){
3503             this.callback.call(this.scope || this);
3504         }
3505         this.callback = null;
3506         this.scope = null;
3507     }
3508 };/*
3509  * Based on:
3510  * Ext JS Library 1.1.1
3511  * Copyright(c) 2006-2007, Ext JS, LLC.
3512  *
3513  * Originally Released Under LGPL - original licence link has changed is not relivant.
3514  *
3515  * Fork - LGPL
3516  * <script type="text/javascript">
3517  */
3518
3519 /**
3520  * @class Roo.dd.DragSource
3521  * @extends Roo.dd.DDProxy
3522  * A simple class that provides the basic implementation needed to make any element draggable.
3523  * @constructor
3524  * @param {String/HTMLElement/Element} el The container element
3525  * @param {Object} config
3526  */
3527 Roo.dd.DragSource = function(el, config){
3528     this.el = Roo.get(el);
3529     this.dragData = {};
3530     
3531     Roo.apply(this, config);
3532     
3533     if(!this.proxy){
3534         this.proxy = new Roo.dd.StatusProxy();
3535     }
3536
3537     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
3538           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
3539     
3540     this.dragging = false;
3541 };
3542
3543 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
3544     /**
3545      * @cfg {String} dropAllowed
3546      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3547      */
3548     dropAllowed : "x-dd-drop-ok",
3549     /**
3550      * @cfg {String} dropNotAllowed
3551      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3552      */
3553     dropNotAllowed : "x-dd-drop-nodrop",
3554
3555     /**
3556      * Returns the data object associated with this drag source
3557      * @return {Object} data An object containing arbitrary data
3558      */
3559     getDragData : function(e){
3560         return this.dragData;
3561     },
3562
3563     // private
3564     onDragEnter : function(e, id){
3565         var target = Roo.dd.DragDropMgr.getDDById(id);
3566         this.cachedTarget = target;
3567         if(this.beforeDragEnter(target, e, id) !== false){
3568             if(target.isNotifyTarget){
3569                 var status = target.notifyEnter(this, e, this.dragData);
3570                 this.proxy.setStatus(status);
3571             }else{
3572                 this.proxy.setStatus(this.dropAllowed);
3573             }
3574             
3575             if(this.afterDragEnter){
3576                 /**
3577                  * An empty function by default, but provided so that you can perform a custom action
3578                  * when the dragged item enters the drop target by providing an implementation.
3579                  * @param {Roo.dd.DragDrop} target The drop target
3580                  * @param {Event} e The event object
3581                  * @param {String} id The id of the dragged element
3582                  * @method afterDragEnter
3583                  */
3584                 this.afterDragEnter(target, e, id);
3585             }
3586         }
3587     },
3588
3589     /**
3590      * An empty function by default, but provided so that you can perform a custom action
3591      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
3592      * @param {Roo.dd.DragDrop} target The drop target
3593      * @param {Event} e The event object
3594      * @param {String} id The id of the dragged element
3595      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3596      */
3597     beforeDragEnter : function(target, e, id){
3598         return true;
3599     },
3600
3601     // private
3602     alignElWithMouse: function() {
3603         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
3604         this.proxy.sync();
3605     },
3606
3607     // private
3608     onDragOver : function(e, id){
3609         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3610         if(this.beforeDragOver(target, e, id) !== false){
3611             if(target.isNotifyTarget){
3612                 var status = target.notifyOver(this, e, this.dragData);
3613                 this.proxy.setStatus(status);
3614             }
3615
3616             if(this.afterDragOver){
3617                 /**
3618                  * An empty function by default, but provided so that you can perform a custom action
3619                  * while the dragged item is over the drop target by providing an implementation.
3620                  * @param {Roo.dd.DragDrop} target The drop target
3621                  * @param {Event} e The event object
3622                  * @param {String} id The id of the dragged element
3623                  * @method afterDragOver
3624                  */
3625                 this.afterDragOver(target, e, id);
3626             }
3627         }
3628     },
3629
3630     /**
3631      * An empty function by default, but provided so that you can perform a custom action
3632      * while the dragged item is over the drop target and optionally cancel the onDragOver.
3633      * @param {Roo.dd.DragDrop} target The drop target
3634      * @param {Event} e The event object
3635      * @param {String} id The id of the dragged element
3636      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3637      */
3638     beforeDragOver : function(target, e, id){
3639         return true;
3640     },
3641
3642     // private
3643     onDragOut : function(e, id){
3644         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3645         if(this.beforeDragOut(target, e, id) !== false){
3646             if(target.isNotifyTarget){
3647                 target.notifyOut(this, e, this.dragData);
3648             }
3649             this.proxy.reset();
3650             if(this.afterDragOut){
3651                 /**
3652                  * An empty function by default, but provided so that you can perform a custom action
3653                  * after the dragged item is dragged out of the target without dropping.
3654                  * @param {Roo.dd.DragDrop} target The drop target
3655                  * @param {Event} e The event object
3656                  * @param {String} id The id of the dragged element
3657                  * @method afterDragOut
3658                  */
3659                 this.afterDragOut(target, e, id);
3660             }
3661         }
3662         this.cachedTarget = null;
3663     },
3664
3665     /**
3666      * An empty function by default, but provided so that you can perform a custom action before the dragged
3667      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
3668      * @param {Roo.dd.DragDrop} target The drop target
3669      * @param {Event} e The event object
3670      * @param {String} id The id of the dragged element
3671      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3672      */
3673     beforeDragOut : function(target, e, id){
3674         return true;
3675     },
3676     
3677     // private
3678     onDragDrop : function(e, id){
3679         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3680         if(this.beforeDragDrop(target, e, id) !== false){
3681             if(target.isNotifyTarget){
3682                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
3683                     this.onValidDrop(target, e, id);
3684                 }else{
3685                     this.onInvalidDrop(target, e, id);
3686                 }
3687             }else{
3688                 this.onValidDrop(target, e, id);
3689             }
3690             
3691             if(this.afterDragDrop){
3692                 /**
3693                  * An empty function by default, but provided so that you can perform a custom action
3694                  * after a valid drag drop has occurred by providing an implementation.
3695                  * @param {Roo.dd.DragDrop} target The drop target
3696                  * @param {Event} e The event object
3697                  * @param {String} id The id of the dropped element
3698                  * @method afterDragDrop
3699                  */
3700                 this.afterDragDrop(target, e, id);
3701             }
3702         }
3703         delete this.cachedTarget;
3704     },
3705
3706     /**
3707      * An empty function by default, but provided so that you can perform a custom action before the dragged
3708      * item is dropped onto the target and optionally cancel the onDragDrop.
3709      * @param {Roo.dd.DragDrop} target The drop target
3710      * @param {Event} e The event object
3711      * @param {String} id The id of the dragged element
3712      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
3713      */
3714     beforeDragDrop : function(target, e, id){
3715         return true;
3716     },
3717
3718     // private
3719     onValidDrop : function(target, e, id){
3720         this.hideProxy();
3721         if(this.afterValidDrop){
3722             /**
3723              * An empty function by default, but provided so that you can perform a custom action
3724              * after a valid drop has occurred by providing an implementation.
3725              * @param {Object} target The target DD 
3726              * @param {Event} e The event object
3727              * @param {String} id The id of the dropped element
3728              * @method afterInvalidDrop
3729              */
3730             this.afterValidDrop(target, e, id);
3731         }
3732     },
3733
3734     // private
3735     getRepairXY : function(e, data){
3736         return this.el.getXY();  
3737     },
3738
3739     // private
3740     onInvalidDrop : function(target, e, id){
3741         this.beforeInvalidDrop(target, e, id);
3742         if(this.cachedTarget){
3743             if(this.cachedTarget.isNotifyTarget){
3744                 this.cachedTarget.notifyOut(this, e, this.dragData);
3745             }
3746             this.cacheTarget = null;
3747         }
3748         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
3749
3750         if(this.afterInvalidDrop){
3751             /**
3752              * An empty function by default, but provided so that you can perform a custom action
3753              * after an invalid drop has occurred by providing an implementation.
3754              * @param {Event} e The event object
3755              * @param {String} id The id of the dropped element
3756              * @method afterInvalidDrop
3757              */
3758             this.afterInvalidDrop(e, id);
3759         }
3760     },
3761
3762     // private
3763     afterRepair : function(){
3764         if(Roo.enableFx){
3765             this.el.highlight(this.hlColor || "c3daf9");
3766         }
3767         this.dragging = false;
3768     },
3769
3770     /**
3771      * An empty function by default, but provided so that you can perform a custom action after an invalid
3772      * drop has occurred.
3773      * @param {Roo.dd.DragDrop} target The drop target
3774      * @param {Event} e The event object
3775      * @param {String} id The id of the dragged element
3776      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
3777      */
3778     beforeInvalidDrop : function(target, e, id){
3779         return true;
3780     },
3781
3782     // private
3783     handleMouseDown : function(e){
3784         if(this.dragging) {
3785             return;
3786         }
3787         var data = this.getDragData(e);
3788         if(data && this.onBeforeDrag(data, e) !== false){
3789             this.dragData = data;
3790             this.proxy.stop();
3791             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
3792         } 
3793     },
3794
3795     /**
3796      * An empty function by default, but provided so that you can perform a custom action before the initial
3797      * drag event begins and optionally cancel it.
3798      * @param {Object} data An object containing arbitrary data to be shared with drop targets
3799      * @param {Event} e The event object
3800      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3801      */
3802     onBeforeDrag : function(data, e){
3803         return true;
3804     },
3805
3806     /**
3807      * An empty function by default, but provided so that you can perform a custom action once the initial
3808      * drag event has begun.  The drag cannot be canceled from this function.
3809      * @param {Number} x The x position of the click on the dragged object
3810      * @param {Number} y The y position of the click on the dragged object
3811      */
3812     onStartDrag : Roo.emptyFn,
3813
3814     // private - YUI override
3815     startDrag : function(x, y){
3816         this.proxy.reset();
3817         this.dragging = true;
3818         this.proxy.update("");
3819         this.onInitDrag(x, y);
3820         this.proxy.show();
3821     },
3822
3823     // private
3824     onInitDrag : function(x, y){
3825         var clone = this.el.dom.cloneNode(true);
3826         clone.id = Roo.id(); // prevent duplicate ids
3827         this.proxy.update(clone);
3828         this.onStartDrag(x, y);
3829         return true;
3830     },
3831
3832     /**
3833      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
3834      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
3835      */
3836     getProxy : function(){
3837         return this.proxy;  
3838     },
3839
3840     /**
3841      * Hides the drag source's {@link Roo.dd.StatusProxy}
3842      */
3843     hideProxy : function(){
3844         this.proxy.hide();  
3845         this.proxy.reset(true);
3846         this.dragging = false;
3847     },
3848
3849     // private
3850     triggerCacheRefresh : function(){
3851         Roo.dd.DDM.refreshCache(this.groups);
3852     },
3853
3854     // private - override to prevent hiding
3855     b4EndDrag: function(e) {
3856     },
3857
3858     // private - override to prevent moving
3859     endDrag : function(e){
3860         this.onEndDrag(this.dragData, e);
3861     },
3862
3863     // private
3864     onEndDrag : function(data, e){
3865     },
3866     
3867     // private - pin to cursor
3868     autoOffset : function(x, y) {
3869         this.setDelta(-12, -20);
3870     }    
3871 });/*
3872  * Based on:
3873  * Ext JS Library 1.1.1
3874  * Copyright(c) 2006-2007, Ext JS, LLC.
3875  *
3876  * Originally Released Under LGPL - original licence link has changed is not relivant.
3877  *
3878  * Fork - LGPL
3879  * <script type="text/javascript">
3880  */
3881
3882
3883 /**
3884  * @class Roo.dd.DropTarget
3885  * @extends Roo.dd.DDTarget
3886  * A simple class that provides the basic implementation needed to make any element a drop target that can have
3887  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
3888  * @constructor
3889  * @param {String/HTMLElement/Element} el The container element
3890  * @param {Object} config
3891  */
3892 Roo.dd.DropTarget = function(el, config){
3893     this.el = Roo.get(el);
3894     
3895     var listeners = false; ;
3896     if (config && config.listeners) {
3897         listeners= config.listeners;
3898         delete config.listeners;
3899     }
3900     Roo.apply(this, config);
3901     
3902     if(this.containerScroll){
3903         Roo.dd.ScrollManager.register(this.el);
3904     }
3905     this.addEvents( {
3906          /**
3907          * @scope Roo.dd.DropTarget
3908          */
3909          
3910          /**
3911          * @event enter
3912          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
3913          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
3914          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
3915          * 
3916          * IMPORTANT : it should set this.overClass and this.dropAllowed
3917          * 
3918          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3919          * @param {Event} e The event
3920          * @param {Object} data An object containing arbitrary data supplied by the drag source
3921          */
3922         "enter" : true,
3923         
3924          /**
3925          * @event over
3926          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
3927          * This method will be called on every mouse movement while the drag source is over the drop target.
3928          * This default implementation simply returns the dropAllowed config value.
3929          * 
3930          * IMPORTANT : it should set this.dropAllowed
3931          * 
3932          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3933          * @param {Event} e The event
3934          * @param {Object} data An object containing arbitrary data supplied by the drag source
3935          
3936          */
3937         "over" : true,
3938         /**
3939          * @event out
3940          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
3941          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
3942          * overClass (if any) from the drop element.
3943          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3944          * @param {Event} e The event
3945          * @param {Object} data An object containing arbitrary data supplied by the drag source
3946          */
3947          "out" : true,
3948          
3949         /**
3950          * @event drop
3951          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
3952          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
3953          * implementation that does something to process the drop event and returns true so that the drag source's
3954          * repair action does not run.
3955          * 
3956          * IMPORTANT : it should set this.success
3957          * 
3958          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3959          * @param {Event} e The event
3960          * @param {Object} data An object containing arbitrary data supplied by the drag source
3961         */
3962          "drop" : true
3963     });
3964             
3965      
3966     Roo.dd.DropTarget.superclass.constructor.call(  this, 
3967         this.el.dom, 
3968         this.ddGroup || this.group,
3969         {
3970             isTarget: true,
3971             listeners : listeners || {} 
3972            
3973         
3974         }
3975     );
3976
3977 };
3978
3979 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
3980     /**
3981      * @cfg {String} overClass
3982      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
3983      */
3984      /**
3985      * @cfg {String} ddGroup
3986      * The drag drop group to handle drop events for
3987      */
3988      
3989     /**
3990      * @cfg {String} dropAllowed
3991      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3992      */
3993     dropAllowed : "x-dd-drop-ok",
3994     /**
3995      * @cfg {String} dropNotAllowed
3996      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3997      */
3998     dropNotAllowed : "x-dd-drop-nodrop",
3999     /**
4000      * @cfg {boolean} success
4001      * set this after drop listener.. 
4002      */
4003     success : false,
4004     /**
4005      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
4006      * if the drop point is valid for over/enter..
4007      */
4008     valid : false,
4009     // private
4010     isTarget : true,
4011
4012     // private
4013     isNotifyTarget : true,
4014     
4015     /**
4016      * @hide
4017      */
4018     notifyEnter : function(dd, e, data)
4019     {
4020         this.valid = true;
4021         this.fireEvent('enter', dd, e, data);
4022         if(this.overClass){
4023             this.el.addClass(this.overClass);
4024         }
4025         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4026             this.valid ? this.dropAllowed : this.dropNotAllowed
4027         );
4028     },
4029
4030     /**
4031      * @hide
4032      */
4033     notifyOver : function(dd, e, data)
4034     {
4035         this.valid = true;
4036         this.fireEvent('over', dd, e, data);
4037         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4038             this.valid ? this.dropAllowed : this.dropNotAllowed
4039         );
4040     },
4041
4042     /**
4043      * @hide
4044      */
4045     notifyOut : function(dd, e, data)
4046     {
4047         this.fireEvent('out', dd, e, data);
4048         if(this.overClass){
4049             this.el.removeClass(this.overClass);
4050         }
4051     },
4052
4053     /**
4054      * @hide
4055      */
4056     notifyDrop : function(dd, e, data)
4057     {
4058         this.success = false;
4059         this.fireEvent('drop', dd, e, data);
4060         return this.success;
4061     }
4062 });/*
4063  * Based on:
4064  * Ext JS Library 1.1.1
4065  * Copyright(c) 2006-2007, Ext JS, LLC.
4066  *
4067  * Originally Released Under LGPL - original licence link has changed is not relivant.
4068  *
4069  * Fork - LGPL
4070  * <script type="text/javascript">
4071  */
4072
4073
4074 /**
4075  * @class Roo.dd.DragZone
4076  * @extends Roo.dd.DragSource
4077  * This class provides a container DD instance that proxies for multiple child node sources.<br />
4078  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
4079  * @constructor
4080  * @param {String/HTMLElement/Element} el The container element
4081  * @param {Object} config
4082  */
4083 Roo.dd.DragZone = function(el, config){
4084     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
4085     if(this.containerScroll){
4086         Roo.dd.ScrollManager.register(this.el);
4087     }
4088 };
4089
4090 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
4091     /**
4092      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
4093      * for auto scrolling during drag operations.
4094      */
4095     /**
4096      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
4097      * method after a failed drop (defaults to "c3daf9" - light blue)
4098      */
4099
4100     /**
4101      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
4102      * for a valid target to drag based on the mouse down. Override this method
4103      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
4104      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
4105      * @param {EventObject} e The mouse down event
4106      * @return {Object} The dragData
4107      */
4108     getDragData : function(e){
4109         return Roo.dd.Registry.getHandleFromEvent(e);
4110     },
4111     
4112     /**
4113      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
4114      * this.dragData.ddel
4115      * @param {Number} x The x position of the click on the dragged object
4116      * @param {Number} y The y position of the click on the dragged object
4117      * @return {Boolean} true to continue the drag, false to cancel
4118      */
4119     onInitDrag : function(x, y){
4120         this.proxy.update(this.dragData.ddel.cloneNode(true));
4121         this.onStartDrag(x, y);
4122         return true;
4123     },
4124     
4125     /**
4126      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
4127      */
4128     afterRepair : function(){
4129         if(Roo.enableFx){
4130             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
4131         }
4132         this.dragging = false;
4133     },
4134
4135     /**
4136      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
4137      * the XY of this.dragData.ddel
4138      * @param {EventObject} e The mouse up event
4139      * @return {Array} The xy location (e.g. [100, 200])
4140      */
4141     getRepairXY : function(e){
4142         return Roo.Element.fly(this.dragData.ddel).getXY();  
4143     }
4144 });/*
4145  * Based on:
4146  * Ext JS Library 1.1.1
4147  * Copyright(c) 2006-2007, Ext JS, LLC.
4148  *
4149  * Originally Released Under LGPL - original licence link has changed is not relivant.
4150  *
4151  * Fork - LGPL
4152  * <script type="text/javascript">
4153  */
4154 /**
4155  * @class Roo.dd.DropZone
4156  * @extends Roo.dd.DropTarget
4157  * This class provides a container DD instance that proxies for multiple child node targets.<br />
4158  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
4159  * @constructor
4160  * @param {String/HTMLElement/Element} el The container element
4161  * @param {Object} config
4162  */
4163 Roo.dd.DropZone = function(el, config){
4164     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
4165 };
4166
4167 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
4168     /**
4169      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
4170      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
4171      * provide your own custom lookup.
4172      * @param {Event} e The event
4173      * @return {Object} data The custom data
4174      */
4175     getTargetFromEvent : function(e){
4176         return Roo.dd.Registry.getTargetFromEvent(e);
4177     },
4178
4179     /**
4180      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
4181      * that it has registered.  This method has no default implementation and should be overridden to provide
4182      * node-specific processing if necessary.
4183      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
4184      * {@link #getTargetFromEvent} for this node)
4185      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4186      * @param {Event} e The event
4187      * @param {Object} data An object containing arbitrary data supplied by the drag source
4188      */
4189     onNodeEnter : function(n, dd, e, data){
4190         
4191     },
4192
4193     /**
4194      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
4195      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
4196      * overridden to provide the proper feedback.
4197      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4198      * {@link #getTargetFromEvent} for this node)
4199      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4200      * @param {Event} e The event
4201      * @param {Object} data An object containing arbitrary data supplied by the drag source
4202      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4203      * underlying {@link Roo.dd.StatusProxy} can be updated
4204      */
4205     onNodeOver : function(n, dd, e, data){
4206         return this.dropAllowed;
4207     },
4208
4209     /**
4210      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
4211      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
4212      * node-specific processing if necessary.
4213      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4214      * {@link #getTargetFromEvent} for this node)
4215      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4216      * @param {Event} e The event
4217      * @param {Object} data An object containing arbitrary data supplied by the drag source
4218      */
4219     onNodeOut : function(n, dd, e, data){
4220         
4221     },
4222
4223     /**
4224      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
4225      * the drop node.  The default implementation returns false, so it should be overridden to provide the
4226      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
4227      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4228      * {@link #getTargetFromEvent} for this node)
4229      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4230      * @param {Event} e The event
4231      * @param {Object} data An object containing arbitrary data supplied by the drag source
4232      * @return {Boolean} True if the drop was valid, else false
4233      */
4234     onNodeDrop : function(n, dd, e, data){
4235         return false;
4236     },
4237
4238     /**
4239      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
4240      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
4241      * it should be overridden to provide the proper feedback if necessary.
4242      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4243      * @param {Event} e The event
4244      * @param {Object} data An object containing arbitrary data supplied by the drag source
4245      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4246      * underlying {@link Roo.dd.StatusProxy} can be updated
4247      */
4248     onContainerOver : function(dd, e, data){
4249         return this.dropNotAllowed;
4250     },
4251
4252     /**
4253      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
4254      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
4255      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
4256      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
4257      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4258      * @param {Event} e The event
4259      * @param {Object} data An object containing arbitrary data supplied by the drag source
4260      * @return {Boolean} True if the drop was valid, else false
4261      */
4262     onContainerDrop : function(dd, e, data){
4263         return false;
4264     },
4265
4266     /**
4267      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
4268      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
4269      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
4270      * you should override this method and provide a custom implementation.
4271      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4272      * @param {Event} e The event
4273      * @param {Object} data An object containing arbitrary data supplied by the drag source
4274      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4275      * underlying {@link Roo.dd.StatusProxy} can be updated
4276      */
4277     notifyEnter : function(dd, e, data){
4278         return this.dropNotAllowed;
4279     },
4280
4281     /**
4282      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
4283      * This method will be called on every mouse movement while the drag source is over the drop zone.
4284      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
4285      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
4286      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
4287      * registered node, it will call {@link #onContainerOver}.
4288      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4289      * @param {Event} e The event
4290      * @param {Object} data An object containing arbitrary data supplied by the drag source
4291      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4292      * underlying {@link Roo.dd.StatusProxy} can be updated
4293      */
4294     notifyOver : function(dd, e, data){
4295         var n = this.getTargetFromEvent(e);
4296         if(!n){ // not over valid drop target
4297             if(this.lastOverNode){
4298                 this.onNodeOut(this.lastOverNode, dd, e, data);
4299                 this.lastOverNode = null;
4300             }
4301             return this.onContainerOver(dd, e, data);
4302         }
4303         if(this.lastOverNode != n){
4304             if(this.lastOverNode){
4305                 this.onNodeOut(this.lastOverNode, dd, e, data);
4306             }
4307             this.onNodeEnter(n, dd, e, data);
4308             this.lastOverNode = n;
4309         }
4310         return this.onNodeOver(n, dd, e, data);
4311     },
4312
4313     /**
4314      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
4315      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
4316      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
4317      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
4318      * @param {Event} e The event
4319      * @param {Object} data An object containing arbitrary data supplied by the drag zone
4320      */
4321     notifyOut : function(dd, e, data){
4322         if(this.lastOverNode){
4323             this.onNodeOut(this.lastOverNode, dd, e, data);
4324             this.lastOverNode = null;
4325         }
4326     },
4327
4328     /**
4329      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
4330      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
4331      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
4332      * otherwise it will call {@link #onContainerDrop}.
4333      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4334      * @param {Event} e The event
4335      * @param {Object} data An object containing arbitrary data supplied by the drag source
4336      * @return {Boolean} True if the drop was valid, else false
4337      */
4338     notifyDrop : function(dd, e, data){
4339         if(this.lastOverNode){
4340             this.onNodeOut(this.lastOverNode, dd, e, data);
4341             this.lastOverNode = null;
4342         }
4343         var n = this.getTargetFromEvent(e);
4344         return n ?
4345             this.onNodeDrop(n, dd, e, data) :
4346             this.onContainerDrop(dd, e, data);
4347     },
4348
4349     // private
4350     triggerCacheRefresh : function(){
4351         Roo.dd.DDM.refreshCache(this.groups);
4352     }  
4353 });/*
4354  * Based on:
4355  * Ext JS Library 1.1.1
4356  * Copyright(c) 2006-2007, Ext JS, LLC.
4357  *
4358  * Originally Released Under LGPL - original licence link has changed is not relivant.
4359  *
4360  * Fork - LGPL
4361  * <script type="text/javascript">
4362  */
4363
4364
4365 /**
4366  * @class Roo.data.SortTypes
4367  * @singleton
4368  * Defines the default sorting (casting?) comparison functions used when sorting data.
4369  */
4370 Roo.data.SortTypes = {
4371     /**
4372      * Default sort that does nothing
4373      * @param {Mixed} s The value being converted
4374      * @return {Mixed} The comparison value
4375      */
4376     none : function(s){
4377         return s;
4378     },
4379     
4380     /**
4381      * The regular expression used to strip tags
4382      * @type {RegExp}
4383      * @property
4384      */
4385     stripTagsRE : /<\/?[^>]+>/gi,
4386     
4387     /**
4388      * Strips all HTML tags to sort on text only
4389      * @param {Mixed} s The value being converted
4390      * @return {String} The comparison value
4391      */
4392     asText : function(s){
4393         return String(s).replace(this.stripTagsRE, "");
4394     },
4395     
4396     /**
4397      * Strips all HTML tags to sort on text only - Case insensitive
4398      * @param {Mixed} s The value being converted
4399      * @return {String} The comparison value
4400      */
4401     asUCText : function(s){
4402         return String(s).toUpperCase().replace(this.stripTagsRE, "");
4403     },
4404     
4405     /**
4406      * Case insensitive string
4407      * @param {Mixed} s The value being converted
4408      * @return {String} The comparison value
4409      */
4410     asUCString : function(s) {
4411         return String(s).toUpperCase();
4412     },
4413     
4414     /**
4415      * Date sorting
4416      * @param {Mixed} s The value being converted
4417      * @return {Number} The comparison value
4418      */
4419     asDate : function(s) {
4420         if(!s){
4421             return 0;
4422         }
4423         if(s instanceof Date){
4424             return s.getTime();
4425         }
4426         return Date.parse(String(s));
4427     },
4428     
4429     /**
4430      * Float sorting
4431      * @param {Mixed} s The value being converted
4432      * @return {Float} The comparison value
4433      */
4434     asFloat : function(s) {
4435         var val = parseFloat(String(s).replace(/,/g, ""));
4436         if(isNaN(val)) val = 0;
4437         return val;
4438     },
4439     
4440     /**
4441      * Integer sorting
4442      * @param {Mixed} s The value being converted
4443      * @return {Number} The comparison value
4444      */
4445     asInt : function(s) {
4446         var val = parseInt(String(s).replace(/,/g, ""));
4447         if(isNaN(val)) val = 0;
4448         return val;
4449     }
4450 };/*
4451  * Based on:
4452  * Ext JS Library 1.1.1
4453  * Copyright(c) 2006-2007, Ext JS, LLC.
4454  *
4455  * Originally Released Under LGPL - original licence link has changed is not relivant.
4456  *
4457  * Fork - LGPL
4458  * <script type="text/javascript">
4459  */
4460
4461 /**
4462 * @class Roo.data.Record
4463  * Instances of this class encapsulate both record <em>definition</em> information, and record
4464  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
4465  * to access Records cached in an {@link Roo.data.Store} object.<br>
4466  * <p>
4467  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
4468  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
4469  * objects.<br>
4470  * <p>
4471  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
4472  * @constructor
4473  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
4474  * {@link #create}. The parameters are the same.
4475  * @param {Array} data An associative Array of data values keyed by the field name.
4476  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
4477  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
4478  * not specified an integer id is generated.
4479  */
4480 Roo.data.Record = function(data, id){
4481     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
4482     this.data = data;
4483 };
4484
4485 /**
4486  * Generate a constructor for a specific record layout.
4487  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
4488  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
4489  * Each field definition object may contain the following properties: <ul>
4490  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
4491  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
4492  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
4493  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
4494  * is being used, then this is a string containing the javascript expression to reference the data relative to 
4495  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
4496  * to the data item relative to the record element. If the mapping expression is the same as the field name,
4497  * this may be omitted.</p></li>
4498  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
4499  * <ul><li>auto (Default, implies no conversion)</li>
4500  * <li>string</li>
4501  * <li>int</li>
4502  * <li>float</li>
4503  * <li>boolean</li>
4504  * <li>date</li></ul></p></li>
4505  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
4506  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
4507  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
4508  * by the Reader into an object that will be stored in the Record. It is passed the
4509  * following parameters:<ul>
4510  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
4511  * </ul></p></li>
4512  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
4513  * </ul>
4514  * <br>usage:<br><pre><code>
4515 var TopicRecord = Roo.data.Record.create(
4516     {name: 'title', mapping: 'topic_title'},
4517     {name: 'author', mapping: 'username'},
4518     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
4519     {name: 'lastPost', mapping: 'post_time', type: 'date'},
4520     {name: 'lastPoster', mapping: 'user2'},
4521     {name: 'excerpt', mapping: 'post_text'}
4522 );
4523
4524 var myNewRecord = new TopicRecord({
4525     title: 'Do my job please',
4526     author: 'noobie',
4527     totalPosts: 1,
4528     lastPost: new Date(),
4529     lastPoster: 'Animal',
4530     excerpt: 'No way dude!'
4531 });
4532 myStore.add(myNewRecord);
4533 </code></pre>
4534  * @method create
4535  * @static
4536  */
4537 Roo.data.Record.create = function(o){
4538     var f = function(){
4539         f.superclass.constructor.apply(this, arguments);
4540     };
4541     Roo.extend(f, Roo.data.Record);
4542     var p = f.prototype;
4543     p.fields = new Roo.util.MixedCollection(false, function(field){
4544         return field.name;
4545     });
4546     for(var i = 0, len = o.length; i < len; i++){
4547         p.fields.add(new Roo.data.Field(o[i]));
4548     }
4549     f.getField = function(name){
4550         return p.fields.get(name);  
4551     };
4552     return f;
4553 };
4554
4555 Roo.data.Record.AUTO_ID = 1000;
4556 Roo.data.Record.EDIT = 'edit';
4557 Roo.data.Record.REJECT = 'reject';
4558 Roo.data.Record.COMMIT = 'commit';
4559
4560 Roo.data.Record.prototype = {
4561     /**
4562      * Readonly flag - true if this record has been modified.
4563      * @type Boolean
4564      */
4565     dirty : false,
4566     editing : false,
4567     error: null,
4568     modified: null,
4569
4570     // private
4571     join : function(store){
4572         this.store = store;
4573     },
4574
4575     /**
4576      * Set the named field to the specified value.
4577      * @param {String} name The name of the field to set.
4578      * @param {Object} value The value to set the field to.
4579      */
4580     set : function(name, value){
4581         if(this.data[name] == value){
4582             return;
4583         }
4584         this.dirty = true;
4585         if(!this.modified){
4586             this.modified = {};
4587         }
4588         if(typeof this.modified[name] == 'undefined'){
4589             this.modified[name] = this.data[name];
4590         }
4591         this.data[name] = value;
4592         if(!this.editing && this.store){
4593             this.store.afterEdit(this);
4594         }       
4595     },
4596
4597     /**
4598      * Get the value of the named field.
4599      * @param {String} name The name of the field to get the value of.
4600      * @return {Object} The value of the field.
4601      */
4602     get : function(name){
4603         return this.data[name]; 
4604     },
4605
4606     // private
4607     beginEdit : function(){
4608         this.editing = true;
4609         this.modified = {}; 
4610     },
4611
4612     // private
4613     cancelEdit : function(){
4614         this.editing = false;
4615         delete this.modified;
4616     },
4617
4618     // private
4619     endEdit : function(){
4620         this.editing = false;
4621         if(this.dirty && this.store){
4622             this.store.afterEdit(this);
4623         }
4624     },
4625
4626     /**
4627      * Usually called by the {@link Roo.data.Store} which owns the Record.
4628      * Rejects all changes made to the Record since either creation, or the last commit operation.
4629      * Modified fields are reverted to their original values.
4630      * <p>
4631      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4632      * of reject operations.
4633      */
4634     reject : function(){
4635         var m = this.modified;
4636         for(var n in m){
4637             if(typeof m[n] != "function"){
4638                 this.data[n] = m[n];
4639             }
4640         }
4641         this.dirty = false;
4642         delete this.modified;
4643         this.editing = false;
4644         if(this.store){
4645             this.store.afterReject(this);
4646         }
4647     },
4648
4649     /**
4650      * Usually called by the {@link Roo.data.Store} which owns the Record.
4651      * Commits all changes made to the Record since either creation, or the last commit operation.
4652      * <p>
4653      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4654      * of commit operations.
4655      */
4656     commit : function(){
4657         this.dirty = false;
4658         delete this.modified;
4659         this.editing = false;
4660         if(this.store){
4661             this.store.afterCommit(this);
4662         }
4663     },
4664
4665     // private
4666     hasError : function(){
4667         return this.error != null;
4668     },
4669
4670     // private
4671     clearError : function(){
4672         this.error = null;
4673     },
4674
4675     /**
4676      * Creates a copy of this record.
4677      * @param {String} id (optional) A new record id if you don't want to use this record's id
4678      * @return {Record}
4679      */
4680     copy : function(newId) {
4681         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
4682     }
4683 };/*
4684  * Based on:
4685  * Ext JS Library 1.1.1
4686  * Copyright(c) 2006-2007, Ext JS, LLC.
4687  *
4688  * Originally Released Under LGPL - original licence link has changed is not relivant.
4689  *
4690  * Fork - LGPL
4691  * <script type="text/javascript">
4692  */
4693
4694
4695
4696 /**
4697  * @class Roo.data.Store
4698  * @extends Roo.util.Observable
4699  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
4700  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
4701  * <p>
4702  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
4703  * has no knowledge of the format of the data returned by the Proxy.<br>
4704  * <p>
4705  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
4706  * instances from the data object. These records are cached and made available through accessor functions.
4707  * @constructor
4708  * Creates a new Store.
4709  * @param {Object} config A config object containing the objects needed for the Store to access data,
4710  * and read the data into Records.
4711  */
4712 Roo.data.Store = function(config){
4713     this.data = new Roo.util.MixedCollection(false);
4714     this.data.getKey = function(o){
4715         return o.id;
4716     };
4717     this.baseParams = {};
4718     // private
4719     this.paramNames = {
4720         "start" : "start",
4721         "limit" : "limit",
4722         "sort" : "sort",
4723         "dir" : "dir",
4724         "multisort" : "_multisort"
4725     };
4726
4727     if(config && config.data){
4728         this.inlineData = config.data;
4729         delete config.data;
4730     }
4731
4732     Roo.apply(this, config);
4733     
4734     if(this.reader){ // reader passed
4735         this.reader = Roo.factory(this.reader, Roo.data);
4736         this.reader.xmodule = this.xmodule || false;
4737         if(!this.recordType){
4738             this.recordType = this.reader.recordType;
4739         }
4740         if(this.reader.onMetaChange){
4741             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
4742         }
4743     }
4744
4745     if(this.recordType){
4746         this.fields = this.recordType.prototype.fields;
4747     }
4748     this.modified = [];
4749
4750     this.addEvents({
4751         /**
4752          * @event datachanged
4753          * Fires when the data cache has changed, and a widget which is using this Store
4754          * as a Record cache should refresh its view.
4755          * @param {Store} this
4756          */
4757         datachanged : true,
4758         /**
4759          * @event metachange
4760          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
4761          * @param {Store} this
4762          * @param {Object} meta The JSON metadata
4763          */
4764         metachange : true,
4765         /**
4766          * @event add
4767          * Fires when Records have been added to the Store
4768          * @param {Store} this
4769          * @param {Roo.data.Record[]} records The array of Records added
4770          * @param {Number} index The index at which the record(s) were added
4771          */
4772         add : true,
4773         /**
4774          * @event remove
4775          * Fires when a Record has been removed from the Store
4776          * @param {Store} this
4777          * @param {Roo.data.Record} record The Record that was removed
4778          * @param {Number} index The index at which the record was removed
4779          */
4780         remove : true,
4781         /**
4782          * @event update
4783          * Fires when a Record has been updated
4784          * @param {Store} this
4785          * @param {Roo.data.Record} record The Record that was updated
4786          * @param {String} operation The update operation being performed.  Value may be one of:
4787          * <pre><code>
4788  Roo.data.Record.EDIT
4789  Roo.data.Record.REJECT
4790  Roo.data.Record.COMMIT
4791          * </code></pre>
4792          */
4793         update : true,
4794         /**
4795          * @event clear
4796          * Fires when the data cache has been cleared.
4797          * @param {Store} this
4798          */
4799         clear : true,
4800         /**
4801          * @event beforeload
4802          * Fires before a request is made for a new data object.  If the beforeload handler returns false
4803          * the load action will be canceled.
4804          * @param {Store} this
4805          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4806          */
4807         beforeload : true,
4808         /**
4809          * @event load
4810          * Fires after a new set of Records has been loaded.
4811          * @param {Store} this
4812          * @param {Roo.data.Record[]} records The Records that were loaded
4813          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4814          */
4815         load : true,
4816         /**
4817          * @event loadexception
4818          * Fires if an exception occurs in the Proxy during loading.
4819          * Called with the signature of the Proxy's "loadexception" event.
4820          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
4821          * 
4822          * @param {Proxy} 
4823          * @param {Object} return from JsonData.reader() - success, totalRecords, records
4824          * @param {Object} load options 
4825          * @param {Object} jsonData from your request (normally this contains the Exception)
4826          */
4827         loadexception : true
4828     });
4829     
4830     if(this.proxy){
4831         this.proxy = Roo.factory(this.proxy, Roo.data);
4832         this.proxy.xmodule = this.xmodule || false;
4833         this.relayEvents(this.proxy,  ["loadexception"]);
4834     }
4835     this.sortToggle = {};
4836     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
4837
4838     Roo.data.Store.superclass.constructor.call(this);
4839
4840     if(this.inlineData){
4841         this.loadData(this.inlineData);
4842         delete this.inlineData;
4843     }
4844 };
4845 Roo.extend(Roo.data.Store, Roo.util.Observable, {
4846      /**
4847     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
4848     * without a remote query - used by combo/forms at present.
4849     */
4850     
4851     /**
4852     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
4853     */
4854     /**
4855     * @cfg {Array} data Inline data to be loaded when the store is initialized.
4856     */
4857     /**
4858     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
4859     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
4860     */
4861     /**
4862     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
4863     * on any HTTP request
4864     */
4865     /**
4866     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
4867     */
4868     /**
4869     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
4870     */
4871     multiSort: false,
4872     /**
4873     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
4874     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
4875     */
4876     remoteSort : false,
4877
4878     /**
4879     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
4880      * loaded or when a record is removed. (defaults to false).
4881     */
4882     pruneModifiedRecords : false,
4883
4884     // private
4885     lastOptions : null,
4886
4887     /**
4888      * Add Records to the Store and fires the add event.
4889      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4890      */
4891     add : function(records){
4892         records = [].concat(records);
4893         for(var i = 0, len = records.length; i < len; i++){
4894             records[i].join(this);
4895         }
4896         var index = this.data.length;
4897         this.data.addAll(records);
4898         this.fireEvent("add", this, records, index);
4899     },
4900
4901     /**
4902      * Remove a Record from the Store and fires the remove event.
4903      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
4904      */
4905     remove : function(record){
4906         var index = this.data.indexOf(record);
4907         this.data.removeAt(index);
4908         if(this.pruneModifiedRecords){
4909             this.modified.remove(record);
4910         }
4911         this.fireEvent("remove", this, record, index);
4912     },
4913
4914     /**
4915      * Remove all Records from the Store and fires the clear event.
4916      */
4917     removeAll : function(){
4918         this.data.clear();
4919         if(this.pruneModifiedRecords){
4920             this.modified = [];
4921         }
4922         this.fireEvent("clear", this);
4923     },
4924
4925     /**
4926      * Inserts Records to the Store at the given index and fires the add event.
4927      * @param {Number} index The start index at which to insert the passed Records.
4928      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4929      */
4930     insert : function(index, records){
4931         records = [].concat(records);
4932         for(var i = 0, len = records.length; i < len; i++){
4933             this.data.insert(index, records[i]);
4934             records[i].join(this);
4935         }
4936         this.fireEvent("add", this, records, index);
4937     },
4938
4939     /**
4940      * Get the index within the cache of the passed Record.
4941      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
4942      * @return {Number} The index of the passed Record. Returns -1 if not found.
4943      */
4944     indexOf : function(record){
4945         return this.data.indexOf(record);
4946     },
4947
4948     /**
4949      * Get the index within the cache of the Record with the passed id.
4950      * @param {String} id The id of the Record to find.
4951      * @return {Number} The index of the Record. Returns -1 if not found.
4952      */
4953     indexOfId : function(id){
4954         return this.data.indexOfKey(id);
4955     },
4956
4957     /**
4958      * Get the Record with the specified id.
4959      * @param {String} id The id of the Record to find.
4960      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
4961      */
4962     getById : function(id){
4963         return this.data.key(id);
4964     },
4965
4966     /**
4967      * Get the Record at the specified index.
4968      * @param {Number} index The index of the Record to find.
4969      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
4970      */
4971     getAt : function(index){
4972         return this.data.itemAt(index);
4973     },
4974
4975     /**
4976      * Returns a range of Records between specified indices.
4977      * @param {Number} startIndex (optional) The starting index (defaults to 0)
4978      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
4979      * @return {Roo.data.Record[]} An array of Records
4980      */
4981     getRange : function(start, end){
4982         return this.data.getRange(start, end);
4983     },
4984
4985     // private
4986     storeOptions : function(o){
4987         o = Roo.apply({}, o);
4988         delete o.callback;
4989         delete o.scope;
4990         this.lastOptions = o;
4991     },
4992
4993     /**
4994      * Loads the Record cache from the configured Proxy using the configured Reader.
4995      * <p>
4996      * If using remote paging, then the first load call must specify the <em>start</em>
4997      * and <em>limit</em> properties in the options.params property to establish the initial
4998      * position within the dataset, and the number of Records to cache on each read from the Proxy.
4999      * <p>
5000      * <strong>It is important to note that for remote data sources, loading is asynchronous,
5001      * and this call will return before the new data has been loaded. Perform any post-processing
5002      * in a callback function, or in a "load" event handler.</strong>
5003      * <p>
5004      * @param {Object} options An object containing properties which control loading options:<ul>
5005      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
5006      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
5007      * passed the following arguments:<ul>
5008      * <li>r : Roo.data.Record[]</li>
5009      * <li>options: Options object from the load call</li>
5010      * <li>success: Boolean success indicator</li></ul></li>
5011      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
5012      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
5013      * </ul>
5014      */
5015     load : function(options){
5016         options = options || {};
5017         if(this.fireEvent("beforeload", this, options) !== false){
5018             this.storeOptions(options);
5019             var p = Roo.apply(options.params || {}, this.baseParams);
5020             // if meta was not loaded from remote source.. try requesting it.
5021             if (!this.reader.metaFromRemote) {
5022                 p._requestMeta = 1;
5023             }
5024             if(this.sortInfo && this.remoteSort){
5025                 var pn = this.paramNames;
5026                 p[pn["sort"]] = this.sortInfo.field;
5027                 p[pn["dir"]] = this.sortInfo.direction;
5028             }
5029             if (this.multiSort) {
5030                 var pn = this.paramNames;
5031                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
5032             }
5033             
5034             this.proxy.load(p, this.reader, this.loadRecords, this, options);
5035         }
5036     },
5037
5038     /**
5039      * Reloads the Record cache from the configured Proxy using the configured Reader and
5040      * the options from the last load operation performed.
5041      * @param {Object} options (optional) An object containing properties which may override the options
5042      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
5043      * the most recently used options are reused).
5044      */
5045     reload : function(options){
5046         this.load(Roo.applyIf(options||{}, this.lastOptions));
5047     },
5048
5049     // private
5050     // Called as a callback by the Reader during a load operation.
5051     loadRecords : function(o, options, success){
5052         if(!o || success === false){
5053             if(success !== false){
5054                 this.fireEvent("load", this, [], options);
5055             }
5056             if(options.callback){
5057                 options.callback.call(options.scope || this, [], options, false);
5058             }
5059             return;
5060         }
5061         // if data returned failure - throw an exception.
5062         if (o.success === false) {
5063             // show a message if no listener is registered.
5064             if (!this.hasListener('loadexception') && typeof(this.reader.jsonData.errorMsg) != 'undefined') {
5065                     Roo.MessageBox.alert("Error loading",this.reader.jsonData.errorMsg);
5066             }
5067             // loadmask wil be hooked into this..
5068             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
5069             return;
5070         }
5071         var r = o.records, t = o.totalRecords || r.length;
5072         if(!options || options.add !== true){
5073             if(this.pruneModifiedRecords){
5074                 this.modified = [];
5075             }
5076             for(var i = 0, len = r.length; i < len; i++){
5077                 r[i].join(this);
5078             }
5079             if(this.snapshot){
5080                 this.data = this.snapshot;
5081                 delete this.snapshot;
5082             }
5083             this.data.clear();
5084             this.data.addAll(r);
5085             this.totalLength = t;
5086             this.applySort();
5087             this.fireEvent("datachanged", this);
5088         }else{
5089             this.totalLength = Math.max(t, this.data.length+r.length);
5090             this.add(r);
5091         }
5092         this.fireEvent("load", this, r, options);
5093         if(options.callback){
5094             options.callback.call(options.scope || this, r, options, true);
5095         }
5096     },
5097
5098
5099     /**
5100      * Loads data from a passed data block. A Reader which understands the format of the data
5101      * must have been configured in the constructor.
5102      * @param {Object} data The data block from which to read the Records.  The format of the data expected
5103      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
5104      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
5105      */
5106     loadData : function(o, append){
5107         var r = this.reader.readRecords(o);
5108         this.loadRecords(r, {add: append}, true);
5109     },
5110
5111     /**
5112      * Gets the number of cached records.
5113      * <p>
5114      * <em>If using paging, this may not be the total size of the dataset. If the data object
5115      * used by the Reader contains the dataset size, then the getTotalCount() function returns
5116      * the data set size</em>
5117      */
5118     getCount : function(){
5119         return this.data.length || 0;
5120     },
5121
5122     /**
5123      * Gets the total number of records in the dataset as returned by the server.
5124      * <p>
5125      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
5126      * the dataset size</em>
5127      */
5128     getTotalCount : function(){
5129         return this.totalLength || 0;
5130     },
5131
5132     /**
5133      * Returns the sort state of the Store as an object with two properties:
5134      * <pre><code>
5135  field {String} The name of the field by which the Records are sorted
5136  direction {String} The sort order, "ASC" or "DESC"
5137      * </code></pre>
5138      */
5139     getSortState : function(){
5140         return this.sortInfo;
5141     },
5142
5143     // private
5144     applySort : function(){
5145         if(this.sortInfo && !this.remoteSort){
5146             var s = this.sortInfo, f = s.field;
5147             var st = this.fields.get(f).sortType;
5148             var fn = function(r1, r2){
5149                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
5150                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5151             };
5152             this.data.sort(s.direction, fn);
5153             if(this.snapshot && this.snapshot != this.data){
5154                 this.snapshot.sort(s.direction, fn);
5155             }
5156         }
5157     },
5158
5159     /**
5160      * Sets the default sort column and order to be used by the next load operation.
5161      * @param {String} fieldName The name of the field to sort by.
5162      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5163      */
5164     setDefaultSort : function(field, dir){
5165         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
5166     },
5167
5168     /**
5169      * Sort the Records.
5170      * If remote sorting is used, the sort is performed on the server, and the cache is
5171      * reloaded. If local sorting is used, the cache is sorted internally.
5172      * @param {String} fieldName The name of the field to sort by.
5173      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5174      */
5175     sort : function(fieldName, dir){
5176         var f = this.fields.get(fieldName);
5177         if(!dir){
5178             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
5179             
5180             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
5181                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
5182             }else{
5183                 dir = f.sortDir;
5184             }
5185         }
5186         this.sortToggle[f.name] = dir;
5187         this.sortInfo = {field: f.name, direction: dir};
5188         if(!this.remoteSort){
5189             this.applySort();
5190             this.fireEvent("datachanged", this);
5191         }else{
5192             this.load(this.lastOptions);
5193         }
5194     },
5195
5196     /**
5197      * Calls the specified function for each of the Records in the cache.
5198      * @param {Function} fn The function to call. The Record is passed as the first parameter.
5199      * Returning <em>false</em> aborts and exits the iteration.
5200      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
5201      */
5202     each : function(fn, scope){
5203         this.data.each(fn, scope);
5204     },
5205
5206     /**
5207      * Gets all records modified since the last commit.  Modified records are persisted across load operations
5208      * (e.g., during paging).
5209      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
5210      */
5211     getModifiedRecords : function(){
5212         return this.modified;
5213     },
5214
5215     // private
5216     createFilterFn : function(property, value, anyMatch){
5217         if(!value.exec){ // not a regex
5218             value = String(value);
5219             if(value.length == 0){
5220                 return false;
5221             }
5222             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
5223         }
5224         return function(r){
5225             return value.test(r.data[property]);
5226         };
5227     },
5228
5229     /**
5230      * Sums the value of <i>property</i> for each record between start and end and returns the result.
5231      * @param {String} property A field on your records
5232      * @param {Number} start The record index to start at (defaults to 0)
5233      * @param {Number} end The last record index to include (defaults to length - 1)
5234      * @return {Number} The sum
5235      */
5236     sum : function(property, start, end){
5237         var rs = this.data.items, v = 0;
5238         start = start || 0;
5239         end = (end || end === 0) ? end : rs.length-1;
5240
5241         for(var i = start; i <= end; i++){
5242             v += (rs[i].data[property] || 0);
5243         }
5244         return v;
5245     },
5246
5247     /**
5248      * Filter the records by a specified property.
5249      * @param {String} field A field on your records
5250      * @param {String/RegExp} value Either a string that the field
5251      * should start with or a RegExp to test against the field
5252      * @param {Boolean} anyMatch True to match any part not just the beginning
5253      */
5254     filter : function(property, value, anyMatch){
5255         var fn = this.createFilterFn(property, value, anyMatch);
5256         return fn ? this.filterBy(fn) : this.clearFilter();
5257     },
5258
5259     /**
5260      * Filter by a function. The specified function will be called with each
5261      * record in this data source. If the function returns true the record is included,
5262      * otherwise it is filtered.
5263      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5264      * @param {Object} scope (optional) The scope of the function (defaults to this)
5265      */
5266     filterBy : function(fn, scope){
5267         this.snapshot = this.snapshot || this.data;
5268         this.data = this.queryBy(fn, scope||this);
5269         this.fireEvent("datachanged", this);
5270     },
5271
5272     /**
5273      * Query the records by a specified property.
5274      * @param {String} field A field on your records
5275      * @param {String/RegExp} value Either a string that the field
5276      * should start with or a RegExp to test against the field
5277      * @param {Boolean} anyMatch True to match any part not just the beginning
5278      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5279      */
5280     query : function(property, value, anyMatch){
5281         var fn = this.createFilterFn(property, value, anyMatch);
5282         return fn ? this.queryBy(fn) : this.data.clone();
5283     },
5284
5285     /**
5286      * Query by a function. The specified function will be called with each
5287      * record in this data source. If the function returns true the record is included
5288      * in the results.
5289      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5290      * @param {Object} scope (optional) The scope of the function (defaults to this)
5291       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5292      **/
5293     queryBy : function(fn, scope){
5294         var data = this.snapshot || this.data;
5295         return data.filterBy(fn, scope||this);
5296     },
5297
5298     /**
5299      * Collects unique values for a particular dataIndex from this store.
5300      * @param {String} dataIndex The property to collect
5301      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
5302      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
5303      * @return {Array} An array of the unique values
5304      **/
5305     collect : function(dataIndex, allowNull, bypassFilter){
5306         var d = (bypassFilter === true && this.snapshot) ?
5307                 this.snapshot.items : this.data.items;
5308         var v, sv, r = [], l = {};
5309         for(var i = 0, len = d.length; i < len; i++){
5310             v = d[i].data[dataIndex];
5311             sv = String(v);
5312             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
5313                 l[sv] = true;
5314                 r[r.length] = v;
5315             }
5316         }
5317         return r;
5318     },
5319
5320     /**
5321      * Revert to a view of the Record cache with no filtering applied.
5322      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
5323      */
5324     clearFilter : function(suppressEvent){
5325         if(this.snapshot && this.snapshot != this.data){
5326             this.data = this.snapshot;
5327             delete this.snapshot;
5328             if(suppressEvent !== true){
5329                 this.fireEvent("datachanged", this);
5330             }
5331         }
5332     },
5333
5334     // private
5335     afterEdit : function(record){
5336         if(this.modified.indexOf(record) == -1){
5337             this.modified.push(record);
5338         }
5339         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
5340     },
5341     
5342     // private
5343     afterReject : function(record){
5344         this.modified.remove(record);
5345         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
5346     },
5347
5348     // private
5349     afterCommit : function(record){
5350         this.modified.remove(record);
5351         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
5352     },
5353
5354     /**
5355      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
5356      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
5357      */
5358     commitChanges : function(){
5359         var m = this.modified.slice(0);
5360         this.modified = [];
5361         for(var i = 0, len = m.length; i < len; i++){
5362             m[i].commit();
5363         }
5364     },
5365
5366     /**
5367      * Cancel outstanding changes on all changed records.
5368      */
5369     rejectChanges : function(){
5370         var m = this.modified.slice(0);
5371         this.modified = [];
5372         for(var i = 0, len = m.length; i < len; i++){
5373             m[i].reject();
5374         }
5375     },
5376
5377     onMetaChange : function(meta, rtype, o){
5378         this.recordType = rtype;
5379         this.fields = rtype.prototype.fields;
5380         delete this.snapshot;
5381         this.sortInfo = meta.sortInfo || this.sortInfo;
5382         this.modified = [];
5383         this.fireEvent('metachange', this, this.reader.meta);
5384     }
5385 });/*
5386  * Based on:
5387  * Ext JS Library 1.1.1
5388  * Copyright(c) 2006-2007, Ext JS, LLC.
5389  *
5390  * Originally Released Under LGPL - original licence link has changed is not relivant.
5391  *
5392  * Fork - LGPL
5393  * <script type="text/javascript">
5394  */
5395
5396 /**
5397  * @class Roo.data.SimpleStore
5398  * @extends Roo.data.Store
5399  * Small helper class to make creating Stores from Array data easier.
5400  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
5401  * @cfg {Array} fields An array of field definition objects, or field name strings.
5402  * @cfg {Array} data The multi-dimensional array of data
5403  * @constructor
5404  * @param {Object} config
5405  */
5406 Roo.data.SimpleStore = function(config){
5407     Roo.data.SimpleStore.superclass.constructor.call(this, {
5408         isLocal : true,
5409         reader: new Roo.data.ArrayReader({
5410                 id: config.id
5411             },
5412             Roo.data.Record.create(config.fields)
5413         ),
5414         proxy : new Roo.data.MemoryProxy(config.data)
5415     });
5416     this.load();
5417 };
5418 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
5419  * Based on:
5420  * Ext JS Library 1.1.1
5421  * Copyright(c) 2006-2007, Ext JS, LLC.
5422  *
5423  * Originally Released Under LGPL - original licence link has changed is not relivant.
5424  *
5425  * Fork - LGPL
5426  * <script type="text/javascript">
5427  */
5428
5429 /**
5430 /**
5431  * @extends Roo.data.Store
5432  * @class Roo.data.JsonStore
5433  * Small helper class to make creating Stores for JSON data easier. <br/>
5434 <pre><code>
5435 var store = new Roo.data.JsonStore({
5436     url: 'get-images.php',
5437     root: 'images',
5438     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
5439 });
5440 </code></pre>
5441  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
5442  * JsonReader and HttpProxy (unless inline data is provided).</b>
5443  * @cfg {Array} fields An array of field definition objects, or field name strings.
5444  * @constructor
5445  * @param {Object} config
5446  */
5447 Roo.data.JsonStore = function(c){
5448     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
5449         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
5450         reader: new Roo.data.JsonReader(c, c.fields)
5451     }));
5452 };
5453 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
5454  * Based on:
5455  * Ext JS Library 1.1.1
5456  * Copyright(c) 2006-2007, Ext JS, LLC.
5457  *
5458  * Originally Released Under LGPL - original licence link has changed is not relivant.
5459  *
5460  * Fork - LGPL
5461  * <script type="text/javascript">
5462  */
5463
5464  
5465 Roo.data.Field = function(config){
5466     if(typeof config == "string"){
5467         config = {name: config};
5468     }
5469     Roo.apply(this, config);
5470     
5471     if(!this.type){
5472         this.type = "auto";
5473     }
5474     
5475     var st = Roo.data.SortTypes;
5476     // named sortTypes are supported, here we look them up
5477     if(typeof this.sortType == "string"){
5478         this.sortType = st[this.sortType];
5479     }
5480     
5481     // set default sortType for strings and dates
5482     if(!this.sortType){
5483         switch(this.type){
5484             case "string":
5485                 this.sortType = st.asUCString;
5486                 break;
5487             case "date":
5488                 this.sortType = st.asDate;
5489                 break;
5490             default:
5491                 this.sortType = st.none;
5492         }
5493     }
5494
5495     // define once
5496     var stripRe = /[\$,%]/g;
5497
5498     // prebuilt conversion function for this field, instead of
5499     // switching every time we're reading a value
5500     if(!this.convert){
5501         var cv, dateFormat = this.dateFormat;
5502         switch(this.type){
5503             case "":
5504             case "auto":
5505             case undefined:
5506                 cv = function(v){ return v; };
5507                 break;
5508             case "string":
5509                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
5510                 break;
5511             case "int":
5512                 cv = function(v){
5513                     return v !== undefined && v !== null && v !== '' ?
5514                            parseInt(String(v).replace(stripRe, ""), 10) : '';
5515                     };
5516                 break;
5517             case "float":
5518                 cv = function(v){
5519                     return v !== undefined && v !== null && v !== '' ?
5520                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
5521                     };
5522                 break;
5523             case "bool":
5524             case "boolean":
5525                 cv = function(v){ return v === true || v === "true" || v == 1; };
5526                 break;
5527             case "date":
5528                 cv = function(v){
5529                     if(!v){
5530                         return '';
5531                     }
5532                     if(v instanceof Date){
5533                         return v;
5534                     }
5535                     if(dateFormat){
5536                         if(dateFormat == "timestamp"){
5537                             return new Date(v*1000);
5538                         }
5539                         return Date.parseDate(v, dateFormat);
5540                     }
5541                     var parsed = Date.parse(v);
5542                     return parsed ? new Date(parsed) : null;
5543                 };
5544              break;
5545             
5546         }
5547         this.convert = cv;
5548     }
5549 };
5550
5551 Roo.data.Field.prototype = {
5552     dateFormat: null,
5553     defaultValue: "",
5554     mapping: null,
5555     sortType : null,
5556     sortDir : "ASC"
5557 };/*
5558  * Based on:
5559  * Ext JS Library 1.1.1
5560  * Copyright(c) 2006-2007, Ext JS, LLC.
5561  *
5562  * Originally Released Under LGPL - original licence link has changed is not relivant.
5563  *
5564  * Fork - LGPL
5565  * <script type="text/javascript">
5566  */
5567  
5568 // Base class for reading structured data from a data source.  This class is intended to be
5569 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
5570
5571 /**
5572  * @class Roo.data.DataReader
5573  * Base class for reading structured data from a data source.  This class is intended to be
5574  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
5575  */
5576
5577 Roo.data.DataReader = function(meta, recordType){
5578     
5579     this.meta = meta;
5580     
5581     this.recordType = recordType instanceof Array ? 
5582         Roo.data.Record.create(recordType) : recordType;
5583 };
5584
5585 Roo.data.DataReader.prototype = {
5586      /**
5587      * Create an empty record
5588      * @param {Object} data (optional) - overlay some values
5589      * @return {Roo.data.Record} record created.
5590      */
5591     newRow :  function(d) {
5592         var da =  {};
5593         this.recordType.prototype.fields.each(function(c) {
5594             switch( c.type) {
5595                 case 'int' : da[c.name] = 0; break;
5596                 case 'date' : da[c.name] = new Date(); break;
5597                 case 'float' : da[c.name] = 0.0; break;
5598                 case 'boolean' : da[c.name] = false; break;
5599                 default : da[c.name] = ""; break;
5600             }
5601             
5602         });
5603         return new this.recordType(Roo.apply(da, d));
5604     }
5605     
5606 };/*
5607  * Based on:
5608  * Ext JS Library 1.1.1
5609  * Copyright(c) 2006-2007, Ext JS, LLC.
5610  *
5611  * Originally Released Under LGPL - original licence link has changed is not relivant.
5612  *
5613  * Fork - LGPL
5614  * <script type="text/javascript">
5615  */
5616
5617 /**
5618  * @class Roo.data.DataProxy
5619  * @extends Roo.data.Observable
5620  * This class is an abstract base class for implementations which provide retrieval of
5621  * unformatted data objects.<br>
5622  * <p>
5623  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
5624  * (of the appropriate type which knows how to parse the data object) to provide a block of
5625  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
5626  * <p>
5627  * Custom implementations must implement the load method as described in
5628  * {@link Roo.data.HttpProxy#load}.
5629  */
5630 Roo.data.DataProxy = function(){
5631     this.addEvents({
5632         /**
5633          * @event beforeload
5634          * Fires before a network request is made to retrieve a data object.
5635          * @param {Object} This DataProxy object.
5636          * @param {Object} params The params parameter to the load function.
5637          */
5638         beforeload : true,
5639         /**
5640          * @event load
5641          * Fires before the load method's callback is called.
5642          * @param {Object} This DataProxy object.
5643          * @param {Object} o The data object.
5644          * @param {Object} arg The callback argument object passed to the load function.
5645          */
5646         load : true,
5647         /**
5648          * @event loadexception
5649          * Fires if an Exception occurs during data retrieval.
5650          * @param {Object} This DataProxy object.
5651          * @param {Object} o The data object.
5652          * @param {Object} arg The callback argument object passed to the load function.
5653          * @param {Object} e The Exception.
5654          */
5655         loadexception : true
5656     });
5657     Roo.data.DataProxy.superclass.constructor.call(this);
5658 };
5659
5660 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
5661
5662     /**
5663      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
5664      */
5665 /*
5666  * Based on:
5667  * Ext JS Library 1.1.1
5668  * Copyright(c) 2006-2007, Ext JS, LLC.
5669  *
5670  * Originally Released Under LGPL - original licence link has changed is not relivant.
5671  *
5672  * Fork - LGPL
5673  * <script type="text/javascript">
5674  */
5675 /**
5676  * @class Roo.data.MemoryProxy
5677  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
5678  * to the Reader when its load method is called.
5679  * @constructor
5680  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
5681  */
5682 Roo.data.MemoryProxy = function(data){
5683     if (data.data) {
5684         data = data.data;
5685     }
5686     Roo.data.MemoryProxy.superclass.constructor.call(this);
5687     this.data = data;
5688 };
5689
5690 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
5691     /**
5692      * Load data from the requested source (in this case an in-memory
5693      * data object passed to the constructor), read the data object into
5694      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5695      * process that block using the passed callback.
5696      * @param {Object} params This parameter is not used by the MemoryProxy class.
5697      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5698      * object into a block of Roo.data.Records.
5699      * @param {Function} callback The function into which to pass the block of Roo.data.records.
5700      * The function must be passed <ul>
5701      * <li>The Record block object</li>
5702      * <li>The "arg" argument from the load function</li>
5703      * <li>A boolean success indicator</li>
5704      * </ul>
5705      * @param {Object} scope The scope in which to call the callback
5706      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5707      */
5708     load : function(params, reader, callback, scope, arg){
5709         params = params || {};
5710         var result;
5711         try {
5712             result = reader.readRecords(this.data);
5713         }catch(e){
5714             this.fireEvent("loadexception", this, arg, null, e);
5715             callback.call(scope, null, arg, false);
5716             return;
5717         }
5718         callback.call(scope, result, arg, true);
5719     },
5720     
5721     // private
5722     update : function(params, records){
5723         
5724     }
5725 });/*
5726  * Based on:
5727  * Ext JS Library 1.1.1
5728  * Copyright(c) 2006-2007, Ext JS, LLC.
5729  *
5730  * Originally Released Under LGPL - original licence link has changed is not relivant.
5731  *
5732  * Fork - LGPL
5733  * <script type="text/javascript">
5734  */
5735 /**
5736  * @class Roo.data.HttpProxy
5737  * @extends Roo.data.DataProxy
5738  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
5739  * configured to reference a certain URL.<br><br>
5740  * <p>
5741  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
5742  * from which the running page was served.<br><br>
5743  * <p>
5744  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
5745  * <p>
5746  * Be aware that to enable the browser to parse an XML document, the server must set
5747  * the Content-Type header in the HTTP response to "text/xml".
5748  * @constructor
5749  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
5750  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
5751  * will be used to make the request.
5752  */
5753 Roo.data.HttpProxy = function(conn){
5754     Roo.data.HttpProxy.superclass.constructor.call(this);
5755     // is conn a conn config or a real conn?
5756     this.conn = conn;
5757     this.useAjax = !conn || !conn.events;
5758   
5759 };
5760
5761 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
5762     // thse are take from connection...
5763     
5764     /**
5765      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
5766      */
5767     /**
5768      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
5769      * extra parameters to each request made by this object. (defaults to undefined)
5770      */
5771     /**
5772      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
5773      *  to each request made by this object. (defaults to undefined)
5774      */
5775     /**
5776      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
5777      */
5778     /**
5779      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
5780      */
5781      /**
5782      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
5783      * @type Boolean
5784      */
5785   
5786
5787     /**
5788      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
5789      * @type Boolean
5790      */
5791     /**
5792      * Return the {@link Roo.data.Connection} object being used by this Proxy.
5793      * @return {Connection} The Connection object. This object may be used to subscribe to events on
5794      * a finer-grained basis than the DataProxy events.
5795      */
5796     getConnection : function(){
5797         return this.useAjax ? Roo.Ajax : this.conn;
5798     },
5799
5800     /**
5801      * Load data from the configured {@link Roo.data.Connection}, read the data object into
5802      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
5803      * process that block using the passed callback.
5804      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5805      * for the request to the remote server.
5806      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5807      * object into a block of Roo.data.Records.
5808      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5809      * The function must be passed <ul>
5810      * <li>The Record block object</li>
5811      * <li>The "arg" argument from the load function</li>
5812      * <li>A boolean success indicator</li>
5813      * </ul>
5814      * @param {Object} scope The scope in which to call the callback
5815      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5816      */
5817     load : function(params, reader, callback, scope, arg){
5818         if(this.fireEvent("beforeload", this, params) !== false){
5819             var  o = {
5820                 params : params || {},
5821                 request: {
5822                     callback : callback,
5823                     scope : scope,
5824                     arg : arg
5825                 },
5826                 reader: reader,
5827                 callback : this.loadResponse,
5828                 scope: this
5829             };
5830             if(this.useAjax){
5831                 Roo.applyIf(o, this.conn);
5832                 if(this.activeRequest){
5833                     Roo.Ajax.abort(this.activeRequest);
5834                 }
5835                 this.activeRequest = Roo.Ajax.request(o);
5836             }else{
5837                 this.conn.request(o);
5838             }
5839         }else{
5840             callback.call(scope||this, null, arg, false);
5841         }
5842     },
5843
5844     // private
5845     loadResponse : function(o, success, response){
5846         delete this.activeRequest;
5847         if(!success){
5848             this.fireEvent("loadexception", this, o, response);
5849             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5850             return;
5851         }
5852         var result;
5853         try {
5854             result = o.reader.read(response);
5855         }catch(e){
5856             this.fireEvent("loadexception", this, o, response, e);
5857             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5858             return;
5859         }
5860         
5861         this.fireEvent("load", this, o, o.request.arg);
5862         o.request.callback.call(o.request.scope, result, o.request.arg, true);
5863     },
5864
5865     // private
5866     update : function(dataSet){
5867
5868     },
5869
5870     // private
5871     updateResponse : function(dataSet){
5872
5873     }
5874 });/*
5875  * Based on:
5876  * Ext JS Library 1.1.1
5877  * Copyright(c) 2006-2007, Ext JS, LLC.
5878  *
5879  * Originally Released Under LGPL - original licence link has changed is not relivant.
5880  *
5881  * Fork - LGPL
5882  * <script type="text/javascript">
5883  */
5884
5885 /**
5886  * @class Roo.data.ScriptTagProxy
5887  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
5888  * other than the originating domain of the running page.<br><br>
5889  * <p>
5890  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
5891  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
5892  * <p>
5893  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
5894  * source code that is used as the source inside a &lt;script> tag.<br><br>
5895  * <p>
5896  * In order for the browser to process the returned data, the server must wrap the data object
5897  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
5898  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
5899  * depending on whether the callback name was passed:
5900  * <p>
5901  * <pre><code>
5902 boolean scriptTag = false;
5903 String cb = request.getParameter("callback");
5904 if (cb != null) {
5905     scriptTag = true;
5906     response.setContentType("text/javascript");
5907 } else {
5908     response.setContentType("application/x-json");
5909 }
5910 Writer out = response.getWriter();
5911 if (scriptTag) {
5912     out.write(cb + "(");
5913 }
5914 out.print(dataBlock.toJsonString());
5915 if (scriptTag) {
5916     out.write(");");
5917 }
5918 </pre></code>
5919  *
5920  * @constructor
5921  * @param {Object} config A configuration object.
5922  */
5923 Roo.data.ScriptTagProxy = function(config){
5924     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
5925     Roo.apply(this, config);
5926     this.head = document.getElementsByTagName("head")[0];
5927 };
5928
5929 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
5930
5931 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
5932     /**
5933      * @cfg {String} url The URL from which to request the data object.
5934      */
5935     /**
5936      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
5937      */
5938     timeout : 30000,
5939     /**
5940      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
5941      * the server the name of the callback function set up by the load call to process the returned data object.
5942      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
5943      * javascript output which calls this named function passing the data object as its only parameter.
5944      */
5945     callbackParam : "callback",
5946     /**
5947      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
5948      * name to the request.
5949      */
5950     nocache : true,
5951
5952     /**
5953      * Load data from the configured URL, read the data object into
5954      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5955      * process that block using the passed callback.
5956      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5957      * for the request to the remote server.
5958      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5959      * object into a block of Roo.data.Records.
5960      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5961      * The function must be passed <ul>
5962      * <li>The Record block object</li>
5963      * <li>The "arg" argument from the load function</li>
5964      * <li>A boolean success indicator</li>
5965      * </ul>
5966      * @param {Object} scope The scope in which to call the callback
5967      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5968      */
5969     load : function(params, reader, callback, scope, arg){
5970         if(this.fireEvent("beforeload", this, params) !== false){
5971
5972             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
5973
5974             var url = this.url;
5975             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
5976             if(this.nocache){
5977                 url += "&_dc=" + (new Date().getTime());
5978             }
5979             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
5980             var trans = {
5981                 id : transId,
5982                 cb : "stcCallback"+transId,
5983                 scriptId : "stcScript"+transId,
5984                 params : params,
5985                 arg : arg,
5986                 url : url,
5987                 callback : callback,
5988                 scope : scope,
5989                 reader : reader
5990             };
5991             var conn = this;
5992
5993             window[trans.cb] = function(o){
5994                 conn.handleResponse(o, trans);
5995             };
5996
5997             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
5998
5999             if(this.autoAbort !== false){
6000                 this.abort();
6001             }
6002
6003             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
6004
6005             var script = document.createElement("script");
6006             script.setAttribute("src", url);
6007             script.setAttribute("type", "text/javascript");
6008             script.setAttribute("id", trans.scriptId);
6009             this.head.appendChild(script);
6010
6011             this.trans = trans;
6012         }else{
6013             callback.call(scope||this, null, arg, false);
6014         }
6015     },
6016
6017     // private
6018     isLoading : function(){
6019         return this.trans ? true : false;
6020     },
6021
6022     /**
6023      * Abort the current server request.
6024      */
6025     abort : function(){
6026         if(this.isLoading()){
6027             this.destroyTrans(this.trans);
6028         }
6029     },
6030
6031     // private
6032     destroyTrans : function(trans, isLoaded){
6033         this.head.removeChild(document.getElementById(trans.scriptId));
6034         clearTimeout(trans.timeoutId);
6035         if(isLoaded){
6036             window[trans.cb] = undefined;
6037             try{
6038                 delete window[trans.cb];
6039             }catch(e){}
6040         }else{
6041             // if hasn't been loaded, wait for load to remove it to prevent script error
6042             window[trans.cb] = function(){
6043                 window[trans.cb] = undefined;
6044                 try{
6045                     delete window[trans.cb];
6046                 }catch(e){}
6047             };
6048         }
6049     },
6050
6051     // private
6052     handleResponse : function(o, trans){
6053         this.trans = false;
6054         this.destroyTrans(trans, true);
6055         var result;
6056         try {
6057             result = trans.reader.readRecords(o);
6058         }catch(e){
6059             this.fireEvent("loadexception", this, o, trans.arg, e);
6060             trans.callback.call(trans.scope||window, null, trans.arg, false);
6061             return;
6062         }
6063         this.fireEvent("load", this, o, trans.arg);
6064         trans.callback.call(trans.scope||window, result, trans.arg, true);
6065     },
6066
6067     // private
6068     handleFailure : function(trans){
6069         this.trans = false;
6070         this.destroyTrans(trans, false);
6071         this.fireEvent("loadexception", this, null, trans.arg);
6072         trans.callback.call(trans.scope||window, null, trans.arg, false);
6073     }
6074 });/*
6075  * Based on:
6076  * Ext JS Library 1.1.1
6077  * Copyright(c) 2006-2007, Ext JS, LLC.
6078  *
6079  * Originally Released Under LGPL - original licence link has changed is not relivant.
6080  *
6081  * Fork - LGPL
6082  * <script type="text/javascript">
6083  */
6084
6085 /**
6086  * @class Roo.data.JsonReader
6087  * @extends Roo.data.DataReader
6088  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
6089  * based on mappings in a provided Roo.data.Record constructor.
6090  * 
6091  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
6092  * in the reply previously. 
6093  * 
6094  * <p>
6095  * Example code:
6096  * <pre><code>
6097 var RecordDef = Roo.data.Record.create([
6098     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6099     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6100 ]);
6101 var myReader = new Roo.data.JsonReader({
6102     totalProperty: "results",    // The property which contains the total dataset size (optional)
6103     root: "rows",                // The property which contains an Array of row objects
6104     id: "id"                     // The property within each row object that provides an ID for the record (optional)
6105 }, RecordDef);
6106 </code></pre>
6107  * <p>
6108  * This would consume a JSON file like this:
6109  * <pre><code>
6110 { 'results': 2, 'rows': [
6111     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
6112     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
6113 }
6114 </code></pre>
6115  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
6116  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6117  * paged from the remote server.
6118  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
6119  * @cfg {String} root name of the property which contains the Array of row objects.
6120  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
6121  * @constructor
6122  * Create a new JsonReader
6123  * @param {Object} meta Metadata configuration options
6124  * @param {Object} recordType Either an Array of field definition objects,
6125  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
6126  */
6127 Roo.data.JsonReader = function(meta, recordType){
6128     
6129     meta = meta || {};
6130     // set some defaults:
6131     Roo.applyIf(meta, {
6132         totalProperty: 'total',
6133         successProperty : 'success',
6134         root : 'data',
6135         id : 'id'
6136     });
6137     
6138     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6139 };
6140 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
6141     
6142     /**
6143      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
6144      * Used by Store query builder to append _requestMeta to params.
6145      * 
6146      */
6147     metaFromRemote : false,
6148     /**
6149      * This method is only used by a DataProxy which has retrieved data from a remote server.
6150      * @param {Object} response The XHR object which contains the JSON data in its responseText.
6151      * @return {Object} data A data block which is used by an Roo.data.Store object as
6152      * a cache of Roo.data.Records.
6153      */
6154     read : function(response){
6155         var json = response.responseText;
6156        
6157         var o = /* eval:var:o */ eval("("+json+")");
6158         if(!o) {
6159             throw {message: "JsonReader.read: Json object not found"};
6160         }
6161         
6162         if(o.metaData){
6163             
6164             delete this.ef;
6165             this.metaFromRemote = true;
6166             this.meta = o.metaData;
6167             this.recordType = Roo.data.Record.create(o.metaData.fields);
6168             this.onMetaChange(this.meta, this.recordType, o);
6169         }
6170         return this.readRecords(o);
6171     },
6172
6173     // private function a store will implement
6174     onMetaChange : function(meta, recordType, o){
6175
6176     },
6177
6178     /**
6179          * @ignore
6180          */
6181     simpleAccess: function(obj, subsc) {
6182         return obj[subsc];
6183     },
6184
6185         /**
6186          * @ignore
6187          */
6188     getJsonAccessor: function(){
6189         var re = /[\[\.]/;
6190         return function(expr) {
6191             try {
6192                 return(re.test(expr))
6193                     ? new Function("obj", "return obj." + expr)
6194                     : function(obj){
6195                         return obj[expr];
6196                     };
6197             } catch(e){}
6198             return Roo.emptyFn;
6199         };
6200     }(),
6201
6202     /**
6203      * Create a data block containing Roo.data.Records from an XML document.
6204      * @param {Object} o An object which contains an Array of row objects in the property specified
6205      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
6206      * which contains the total size of the dataset.
6207      * @return {Object} data A data block which is used by an Roo.data.Store object as
6208      * a cache of Roo.data.Records.
6209      */
6210     readRecords : function(o){
6211         /**
6212          * After any data loads, the raw JSON data is available for further custom processing.
6213          * @type Object
6214          */
6215         this.jsonData = o;
6216         var s = this.meta, Record = this.recordType,
6217             f = Record.prototype.fields, fi = f.items, fl = f.length;
6218
6219 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
6220         if (!this.ef) {
6221             if(s.totalProperty) {
6222                     this.getTotal = this.getJsonAccessor(s.totalProperty);
6223                 }
6224                 if(s.successProperty) {
6225                     this.getSuccess = this.getJsonAccessor(s.successProperty);
6226                 }
6227                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
6228                 if (s.id) {
6229                         var g = this.getJsonAccessor(s.id);
6230                         this.getId = function(rec) {
6231                                 var r = g(rec);
6232                                 return (r === undefined || r === "") ? null : r;
6233                         };
6234                 } else {
6235                         this.getId = function(){return null;};
6236                 }
6237             this.ef = [];
6238             for(var jj = 0; jj < fl; jj++){
6239                 f = fi[jj];
6240                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
6241                 this.ef[jj] = this.getJsonAccessor(map);
6242             }
6243         }
6244
6245         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
6246         if(s.totalProperty){
6247             var vt = parseInt(this.getTotal(o), 10);
6248             if(!isNaN(vt)){
6249                 totalRecords = vt;
6250             }
6251         }
6252         if(s.successProperty){
6253             var vs = this.getSuccess(o);
6254             if(vs === false || vs === 'false'){
6255                 success = false;
6256             }
6257         }
6258         var records = [];
6259             for(var i = 0; i < c; i++){
6260                     var n = root[i];
6261                 var values = {};
6262                 var id = this.getId(n);
6263                 for(var j = 0; j < fl; j++){
6264                     f = fi[j];
6265                 var v = this.ef[j](n);
6266                 if (!f.convert) {
6267                     Roo.log('missing convert for ' + f.name);
6268                     Roo.log(f);
6269                     continue;
6270                 }
6271                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
6272                 }
6273                 var record = new Record(values, id);
6274                 record.json = n;
6275                 records[i] = record;
6276             }
6277             return {
6278                 success : success,
6279                 records : records,
6280                 totalRecords : totalRecords
6281             };
6282     }
6283 });/*
6284  * Based on:
6285  * Ext JS Library 1.1.1
6286  * Copyright(c) 2006-2007, Ext JS, LLC.
6287  *
6288  * Originally Released Under LGPL - original licence link has changed is not relivant.
6289  *
6290  * Fork - LGPL
6291  * <script type="text/javascript">
6292  */
6293
6294 /**
6295  * @class Roo.data.XmlReader
6296  * @extends Roo.data.DataReader
6297  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
6298  * based on mappings in a provided Roo.data.Record constructor.<br><br>
6299  * <p>
6300  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
6301  * header in the HTTP response must be set to "text/xml".</em>
6302  * <p>
6303  * Example code:
6304  * <pre><code>
6305 var RecordDef = Roo.data.Record.create([
6306    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6307    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6308 ]);
6309 var myReader = new Roo.data.XmlReader({
6310    totalRecords: "results", // The element which contains the total dataset size (optional)
6311    record: "row",           // The repeated element which contains row information
6312    id: "id"                 // The element within the row that provides an ID for the record (optional)
6313 }, RecordDef);
6314 </code></pre>
6315  * <p>
6316  * This would consume an XML file like this:
6317  * <pre><code>
6318 &lt;?xml?>
6319 &lt;dataset>
6320  &lt;results>2&lt;/results>
6321  &lt;row>
6322    &lt;id>1&lt;/id>
6323    &lt;name>Bill&lt;/name>
6324    &lt;occupation>Gardener&lt;/occupation>
6325  &lt;/row>
6326  &lt;row>
6327    &lt;id>2&lt;/id>
6328    &lt;name>Ben&lt;/name>
6329    &lt;occupation>Horticulturalist&lt;/occupation>
6330  &lt;/row>
6331 &lt;/dataset>
6332 </code></pre>
6333  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
6334  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6335  * paged from the remote server.
6336  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
6337  * @cfg {String} success The DomQuery path to the success attribute used by forms.
6338  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
6339  * a record identifier value.
6340  * @constructor
6341  * Create a new XmlReader
6342  * @param {Object} meta Metadata configuration options
6343  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
6344  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
6345  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
6346  */
6347 Roo.data.XmlReader = function(meta, recordType){
6348     meta = meta || {};
6349     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6350 };
6351 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
6352     /**
6353      * This method is only used by a DataProxy which has retrieved data from a remote server.
6354          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
6355          * to contain a method called 'responseXML' that returns an XML document object.
6356      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6357      * a cache of Roo.data.Records.
6358      */
6359     read : function(response){
6360         var doc = response.responseXML;
6361         if(!doc) {
6362             throw {message: "XmlReader.read: XML Document not available"};
6363         }
6364         return this.readRecords(doc);
6365     },
6366
6367     /**
6368      * Create a data block containing Roo.data.Records from an XML document.
6369          * @param {Object} doc A parsed XML document.
6370      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6371      * a cache of Roo.data.Records.
6372      */
6373     readRecords : function(doc){
6374         /**
6375          * After any data loads/reads, the raw XML Document is available for further custom processing.
6376          * @type XMLDocument
6377          */
6378         this.xmlData = doc;
6379         var root = doc.documentElement || doc;
6380         var q = Roo.DomQuery;
6381         var recordType = this.recordType, fields = recordType.prototype.fields;
6382         var sid = this.meta.id;
6383         var totalRecords = 0, success = true;
6384         if(this.meta.totalRecords){
6385             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
6386         }
6387         
6388         if(this.meta.success){
6389             var sv = q.selectValue(this.meta.success, root, true);
6390             success = sv !== false && sv !== 'false';
6391         }
6392         var records = [];
6393         var ns = q.select(this.meta.record, root);
6394         for(var i = 0, len = ns.length; i < len; i++) {
6395                 var n = ns[i];
6396                 var values = {};
6397                 var id = sid ? q.selectValue(sid, n) : undefined;
6398                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6399                     var f = fields.items[j];
6400                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
6401                     v = f.convert(v);
6402                     values[f.name] = v;
6403                 }
6404                 var record = new recordType(values, id);
6405                 record.node = n;
6406                 records[records.length] = record;
6407             }
6408
6409             return {
6410                 success : success,
6411                 records : records,
6412                 totalRecords : totalRecords || records.length
6413             };
6414     }
6415 });/*
6416  * Based on:
6417  * Ext JS Library 1.1.1
6418  * Copyright(c) 2006-2007, Ext JS, LLC.
6419  *
6420  * Originally Released Under LGPL - original licence link has changed is not relivant.
6421  *
6422  * Fork - LGPL
6423  * <script type="text/javascript">
6424  */
6425
6426 /**
6427  * @class Roo.data.ArrayReader
6428  * @extends Roo.data.DataReader
6429  * Data reader class to create an Array of Roo.data.Record objects from an Array.
6430  * Each element of that Array represents a row of data fields. The
6431  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
6432  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
6433  * <p>
6434  * Example code:.
6435  * <pre><code>
6436 var RecordDef = Roo.data.Record.create([
6437     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
6438     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
6439 ]);
6440 var myReader = new Roo.data.ArrayReader({
6441     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
6442 }, RecordDef);
6443 </code></pre>
6444  * <p>
6445  * This would consume an Array like this:
6446  * <pre><code>
6447 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
6448   </code></pre>
6449  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
6450  * @constructor
6451  * Create a new JsonReader
6452  * @param {Object} meta Metadata configuration options.
6453  * @param {Object} recordType Either an Array of field definition objects
6454  * as specified to {@link Roo.data.Record#create},
6455  * or an {@link Roo.data.Record} object
6456  * created using {@link Roo.data.Record#create}.
6457  */
6458 Roo.data.ArrayReader = function(meta, recordType){
6459     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
6460 };
6461
6462 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
6463     /**
6464      * Create a data block containing Roo.data.Records from an XML document.
6465      * @param {Object} o An Array of row objects which represents the dataset.
6466      * @return {Object} data A data block which is used by an Roo.data.Store object as
6467      * a cache of Roo.data.Records.
6468      */
6469     readRecords : function(o){
6470         var sid = this.meta ? this.meta.id : null;
6471         var recordType = this.recordType, fields = recordType.prototype.fields;
6472         var records = [];
6473         var root = o;
6474             for(var i = 0; i < root.length; i++){
6475                     var n = root[i];
6476                 var values = {};
6477                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
6478                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6479                 var f = fields.items[j];
6480                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
6481                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
6482                 v = f.convert(v);
6483                 values[f.name] = v;
6484             }
6485                 var record = new recordType(values, id);
6486                 record.json = n;
6487                 records[records.length] = record;
6488             }
6489             return {
6490                 records : records,
6491                 totalRecords : records.length
6492             };
6493     }
6494 });/*
6495  * Based on:
6496  * Ext JS Library 1.1.1
6497  * Copyright(c) 2006-2007, Ext JS, LLC.
6498  *
6499  * Originally Released Under LGPL - original licence link has changed is not relivant.
6500  *
6501  * Fork - LGPL
6502  * <script type="text/javascript">
6503  */
6504
6505
6506 /**
6507  * @class Roo.data.Tree
6508  * @extends Roo.util.Observable
6509  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
6510  * in the tree have most standard DOM functionality.
6511  * @constructor
6512  * @param {Node} root (optional) The root node
6513  */
6514 Roo.data.Tree = function(root){
6515    this.nodeHash = {};
6516    /**
6517     * The root node for this tree
6518     * @type Node
6519     */
6520    this.root = null;
6521    if(root){
6522        this.setRootNode(root);
6523    }
6524    this.addEvents({
6525        /**
6526         * @event append
6527         * Fires when a new child node is appended to a node in this tree.
6528         * @param {Tree} tree The owner tree
6529         * @param {Node} parent The parent node
6530         * @param {Node} node The newly appended node
6531         * @param {Number} index The index of the newly appended node
6532         */
6533        "append" : true,
6534        /**
6535         * @event remove
6536         * Fires when a child node is removed from a node in this tree.
6537         * @param {Tree} tree The owner tree
6538         * @param {Node} parent The parent node
6539         * @param {Node} node The child node removed
6540         */
6541        "remove" : true,
6542        /**
6543         * @event move
6544         * Fires when a node is moved to a new location in the tree
6545         * @param {Tree} tree The owner tree
6546         * @param {Node} node The node moved
6547         * @param {Node} oldParent The old parent of this node
6548         * @param {Node} newParent The new parent of this node
6549         * @param {Number} index The index it was moved to
6550         */
6551        "move" : true,
6552        /**
6553         * @event insert
6554         * Fires when a new child node is inserted in a node in this tree.
6555         * @param {Tree} tree The owner tree
6556         * @param {Node} parent The parent node
6557         * @param {Node} node The child node inserted
6558         * @param {Node} refNode The child node the node was inserted before
6559         */
6560        "insert" : true,
6561        /**
6562         * @event beforeappend
6563         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
6564         * @param {Tree} tree The owner tree
6565         * @param {Node} parent The parent node
6566         * @param {Node} node The child node to be appended
6567         */
6568        "beforeappend" : true,
6569        /**
6570         * @event beforeremove
6571         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
6572         * @param {Tree} tree The owner tree
6573         * @param {Node} parent The parent node
6574         * @param {Node} node The child node to be removed
6575         */
6576        "beforeremove" : true,
6577        /**
6578         * @event beforemove
6579         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
6580         * @param {Tree} tree The owner tree
6581         * @param {Node} node The node being moved
6582         * @param {Node} oldParent The parent of the node
6583         * @param {Node} newParent The new parent the node is moving to
6584         * @param {Number} index The index it is being moved to
6585         */
6586        "beforemove" : true,
6587        /**
6588         * @event beforeinsert
6589         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
6590         * @param {Tree} tree The owner tree
6591         * @param {Node} parent The parent node
6592         * @param {Node} node The child node to be inserted
6593         * @param {Node} refNode The child node the node is being inserted before
6594         */
6595        "beforeinsert" : true
6596    });
6597
6598     Roo.data.Tree.superclass.constructor.call(this);
6599 };
6600
6601 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
6602     pathSeparator: "/",
6603
6604     proxyNodeEvent : function(){
6605         return this.fireEvent.apply(this, arguments);
6606     },
6607
6608     /**
6609      * Returns the root node for this tree.
6610      * @return {Node}
6611      */
6612     getRootNode : function(){
6613         return this.root;
6614     },
6615
6616     /**
6617      * Sets the root node for this tree.
6618      * @param {Node} node
6619      * @return {Node}
6620      */
6621     setRootNode : function(node){
6622         this.root = node;
6623         node.ownerTree = this;
6624         node.isRoot = true;
6625         this.registerNode(node);
6626         return node;
6627     },
6628
6629     /**
6630      * Gets a node in this tree by its id.
6631      * @param {String} id
6632      * @return {Node}
6633      */
6634     getNodeById : function(id){
6635         return this.nodeHash[id];
6636     },
6637
6638     registerNode : function(node){
6639         this.nodeHash[node.id] = node;
6640     },
6641
6642     unregisterNode : function(node){
6643         delete this.nodeHash[node.id];
6644     },
6645
6646     toString : function(){
6647         return "[Tree"+(this.id?" "+this.id:"")+"]";
6648     }
6649 });
6650
6651 /**
6652  * @class Roo.data.Node
6653  * @extends Roo.util.Observable
6654  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
6655  * @cfg {String} id The id for this node. If one is not specified, one is generated.
6656  * @constructor
6657  * @param {Object} attributes The attributes/config for the node
6658  */
6659 Roo.data.Node = function(attributes){
6660     /**
6661      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
6662      * @type {Object}
6663      */
6664     this.attributes = attributes || {};
6665     this.leaf = this.attributes.leaf;
6666     /**
6667      * The node id. @type String
6668      */
6669     this.id = this.attributes.id;
6670     if(!this.id){
6671         this.id = Roo.id(null, "ynode-");
6672         this.attributes.id = this.id;
6673     }
6674     /**
6675      * All child nodes of this node. @type Array
6676      */
6677     this.childNodes = [];
6678     if(!this.childNodes.indexOf){ // indexOf is a must
6679         this.childNodes.indexOf = function(o){
6680             for(var i = 0, len = this.length; i < len; i++){
6681                 if(this[i] == o) {
6682                     return i;
6683                 }
6684             }
6685             return -1;
6686         };
6687     }
6688     /**
6689      * The parent node for this node. @type Node
6690      */
6691     this.parentNode = null;
6692     /**
6693      * The first direct child node of this node, or null if this node has no child nodes. @type Node
6694      */
6695     this.firstChild = null;
6696     /**
6697      * The last direct child node of this node, or null if this node has no child nodes. @type Node
6698      */
6699     this.lastChild = null;
6700     /**
6701      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
6702      */
6703     this.previousSibling = null;
6704     /**
6705      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
6706      */
6707     this.nextSibling = null;
6708
6709     this.addEvents({
6710        /**
6711         * @event append
6712         * Fires when a new child node is appended
6713         * @param {Tree} tree The owner tree
6714         * @param {Node} this This node
6715         * @param {Node} node The newly appended node
6716         * @param {Number} index The index of the newly appended node
6717         */
6718        "append" : true,
6719        /**
6720         * @event remove
6721         * Fires when a child node is removed
6722         * @param {Tree} tree The owner tree
6723         * @param {Node} this This node
6724         * @param {Node} node The removed node
6725         */
6726        "remove" : true,
6727        /**
6728         * @event move
6729         * Fires when this node is moved to a new location in the tree
6730         * @param {Tree} tree The owner tree
6731         * @param {Node} this This node
6732         * @param {Node} oldParent The old parent of this node
6733         * @param {Node} newParent The new parent of this node
6734         * @param {Number} index The index it was moved to
6735         */
6736        "move" : true,
6737        /**
6738         * @event insert
6739         * Fires when a new child node is inserted.
6740         * @param {Tree} tree The owner tree
6741         * @param {Node} this This node
6742         * @param {Node} node The child node inserted
6743         * @param {Node} refNode The child node the node was inserted before
6744         */
6745        "insert" : true,
6746        /**
6747         * @event beforeappend
6748         * Fires before a new child is appended, return false to cancel the append.
6749         * @param {Tree} tree The owner tree
6750         * @param {Node} this This node
6751         * @param {Node} node The child node to be appended
6752         */
6753        "beforeappend" : true,
6754        /**
6755         * @event beforeremove
6756         * Fires before a child is removed, return false to cancel the remove.
6757         * @param {Tree} tree The owner tree
6758         * @param {Node} this This node
6759         * @param {Node} node The child node to be removed
6760         */
6761        "beforeremove" : true,
6762        /**
6763         * @event beforemove
6764         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
6765         * @param {Tree} tree The owner tree
6766         * @param {Node} this This node
6767         * @param {Node} oldParent The parent of this node
6768         * @param {Node} newParent The new parent this node is moving to
6769         * @param {Number} index The index it is being moved to
6770         */
6771        "beforemove" : true,
6772        /**
6773         * @event beforeinsert
6774         * Fires before a new child is inserted, return false to cancel the insert.
6775         * @param {Tree} tree The owner tree
6776         * @param {Node} this This node
6777         * @param {Node} node The child node to be inserted
6778         * @param {Node} refNode The child node the node is being inserted before
6779         */
6780        "beforeinsert" : true
6781    });
6782     this.listeners = this.attributes.listeners;
6783     Roo.data.Node.superclass.constructor.call(this);
6784 };
6785
6786 Roo.extend(Roo.data.Node, Roo.util.Observable, {
6787     fireEvent : function(evtName){
6788         // first do standard event for this node
6789         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
6790             return false;
6791         }
6792         // then bubble it up to the tree if the event wasn't cancelled
6793         var ot = this.getOwnerTree();
6794         if(ot){
6795             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
6796                 return false;
6797             }
6798         }
6799         return true;
6800     },
6801
6802     /**
6803      * Returns true if this node is a leaf
6804      * @return {Boolean}
6805      */
6806     isLeaf : function(){
6807         return this.leaf === true;
6808     },
6809
6810     // private
6811     setFirstChild : function(node){
6812         this.firstChild = node;
6813     },
6814
6815     //private
6816     setLastChild : function(node){
6817         this.lastChild = node;
6818     },
6819
6820
6821     /**
6822      * Returns true if this node is the last child of its parent
6823      * @return {Boolean}
6824      */
6825     isLast : function(){
6826        return (!this.parentNode ? true : this.parentNode.lastChild == this);
6827     },
6828
6829     /**
6830      * Returns true if this node is the first child of its parent
6831      * @return {Boolean}
6832      */
6833     isFirst : function(){
6834        return (!this.parentNode ? true : this.parentNode.firstChild == this);
6835     },
6836
6837     hasChildNodes : function(){
6838         return !this.isLeaf() && this.childNodes.length > 0;
6839     },
6840
6841     /**
6842      * Insert node(s) as the last child node of this node.
6843      * @param {Node/Array} node The node or Array of nodes to append
6844      * @return {Node} The appended node if single append, or null if an array was passed
6845      */
6846     appendChild : function(node){
6847         var multi = false;
6848         if(node instanceof Array){
6849             multi = node;
6850         }else if(arguments.length > 1){
6851             multi = arguments;
6852         }
6853         // if passed an array or multiple args do them one by one
6854         if(multi){
6855             for(var i = 0, len = multi.length; i < len; i++) {
6856                 this.appendChild(multi[i]);
6857             }
6858         }else{
6859             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
6860                 return false;
6861             }
6862             var index = this.childNodes.length;
6863             var oldParent = node.parentNode;
6864             // it's a move, make sure we move it cleanly
6865             if(oldParent){
6866                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
6867                     return false;
6868                 }
6869                 oldParent.removeChild(node);
6870             }
6871             index = this.childNodes.length;
6872             if(index == 0){
6873                 this.setFirstChild(node);
6874             }
6875             this.childNodes.push(node);
6876             node.parentNode = this;
6877             var ps = this.childNodes[index-1];
6878             if(ps){
6879                 node.previousSibling = ps;
6880                 ps.nextSibling = node;
6881             }else{
6882                 node.previousSibling = null;
6883             }
6884             node.nextSibling = null;
6885             this.setLastChild(node);
6886             node.setOwnerTree(this.getOwnerTree());
6887             this.fireEvent("append", this.ownerTree, this, node, index);
6888             if(oldParent){
6889                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
6890             }
6891             return node;
6892         }
6893     },
6894
6895     /**
6896      * Removes a child node from this node.
6897      * @param {Node} node The node to remove
6898      * @return {Node} The removed node
6899      */
6900     removeChild : function(node){
6901         var index = this.childNodes.indexOf(node);
6902         if(index == -1){
6903             return false;
6904         }
6905         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
6906             return false;
6907         }
6908
6909         // remove it from childNodes collection
6910         this.childNodes.splice(index, 1);
6911
6912         // update siblings
6913         if(node.previousSibling){
6914             node.previousSibling.nextSibling = node.nextSibling;
6915         }
6916         if(node.nextSibling){
6917             node.nextSibling.previousSibling = node.previousSibling;
6918         }
6919
6920         // update child refs
6921         if(this.firstChild == node){
6922             this.setFirstChild(node.nextSibling);
6923         }
6924         if(this.lastChild == node){
6925             this.setLastChild(node.previousSibling);
6926         }
6927
6928         node.setOwnerTree(null);
6929         // clear any references from the node
6930         node.parentNode = null;
6931         node.previousSibling = null;
6932         node.nextSibling = null;
6933         this.fireEvent("remove", this.ownerTree, this, node);
6934         return node;
6935     },
6936
6937     /**
6938      * Inserts the first node before the second node in this nodes childNodes collection.
6939      * @param {Node} node The node to insert
6940      * @param {Node} refNode The node to insert before (if null the node is appended)
6941      * @return {Node} The inserted node
6942      */
6943     insertBefore : function(node, refNode){
6944         if(!refNode){ // like standard Dom, refNode can be null for append
6945             return this.appendChild(node);
6946         }
6947         // nothing to do
6948         if(node == refNode){
6949             return false;
6950         }
6951
6952         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
6953             return false;
6954         }
6955         var index = this.childNodes.indexOf(refNode);
6956         var oldParent = node.parentNode;
6957         var refIndex = index;
6958
6959         // when moving internally, indexes will change after remove
6960         if(oldParent == this && this.childNodes.indexOf(node) < index){
6961             refIndex--;
6962         }
6963
6964         // it's a move, make sure we move it cleanly
6965         if(oldParent){
6966             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
6967                 return false;
6968             }
6969             oldParent.removeChild(node);
6970         }
6971         if(refIndex == 0){
6972             this.setFirstChild(node);
6973         }
6974         this.childNodes.splice(refIndex, 0, node);
6975         node.parentNode = this;
6976         var ps = this.childNodes[refIndex-1];
6977         if(ps){
6978             node.previousSibling = ps;
6979             ps.nextSibling = node;
6980         }else{
6981             node.previousSibling = null;
6982         }
6983         node.nextSibling = refNode;
6984         refNode.previousSibling = node;
6985         node.setOwnerTree(this.getOwnerTree());
6986         this.fireEvent("insert", this.ownerTree, this, node, refNode);
6987         if(oldParent){
6988             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
6989         }
6990         return node;
6991     },
6992
6993     /**
6994      * Returns the child node at the specified index.
6995      * @param {Number} index
6996      * @return {Node}
6997      */
6998     item : function(index){
6999         return this.childNodes[index];
7000     },
7001
7002     /**
7003      * Replaces one child node in this node with another.
7004      * @param {Node} newChild The replacement node
7005      * @param {Node} oldChild The node to replace
7006      * @return {Node} The replaced node
7007      */
7008     replaceChild : function(newChild, oldChild){
7009         this.insertBefore(newChild, oldChild);
7010         this.removeChild(oldChild);
7011         return oldChild;
7012     },
7013
7014     /**
7015      * Returns the index of a child node
7016      * @param {Node} node
7017      * @return {Number} The index of the node or -1 if it was not found
7018      */
7019     indexOf : function(child){
7020         return this.childNodes.indexOf(child);
7021     },
7022
7023     /**
7024      * Returns the tree this node is in.
7025      * @return {Tree}
7026      */
7027     getOwnerTree : function(){
7028         // if it doesn't have one, look for one
7029         if(!this.ownerTree){
7030             var p = this;
7031             while(p){
7032                 if(p.ownerTree){
7033                     this.ownerTree = p.ownerTree;
7034                     break;
7035                 }
7036                 p = p.parentNode;
7037             }
7038         }
7039         return this.ownerTree;
7040     },
7041
7042     /**
7043      * Returns depth of this node (the root node has a depth of 0)
7044      * @return {Number}
7045      */
7046     getDepth : function(){
7047         var depth = 0;
7048         var p = this;
7049         while(p.parentNode){
7050             ++depth;
7051             p = p.parentNode;
7052         }
7053         return depth;
7054     },
7055
7056     // private
7057     setOwnerTree : function(tree){
7058         // if it's move, we need to update everyone
7059         if(tree != this.ownerTree){
7060             if(this.ownerTree){
7061                 this.ownerTree.unregisterNode(this);
7062             }
7063             this.ownerTree = tree;
7064             var cs = this.childNodes;
7065             for(var i = 0, len = cs.length; i < len; i++) {
7066                 cs[i].setOwnerTree(tree);
7067             }
7068             if(tree){
7069                 tree.registerNode(this);
7070             }
7071         }
7072     },
7073
7074     /**
7075      * Returns the path for this node. The path can be used to expand or select this node programmatically.
7076      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
7077      * @return {String} The path
7078      */
7079     getPath : function(attr){
7080         attr = attr || "id";
7081         var p = this.parentNode;
7082         var b = [this.attributes[attr]];
7083         while(p){
7084             b.unshift(p.attributes[attr]);
7085             p = p.parentNode;
7086         }
7087         var sep = this.getOwnerTree().pathSeparator;
7088         return sep + b.join(sep);
7089     },
7090
7091     /**
7092      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7093      * function call will be the scope provided or the current node. The arguments to the function
7094      * will be the args provided or the current node. If the function returns false at any point,
7095      * the bubble is stopped.
7096      * @param {Function} fn The function to call
7097      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7098      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7099      */
7100     bubble : function(fn, scope, args){
7101         var p = this;
7102         while(p){
7103             if(fn.call(scope || p, args || p) === false){
7104                 break;
7105             }
7106             p = p.parentNode;
7107         }
7108     },
7109
7110     /**
7111      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7112      * function call will be the scope provided or the current node. The arguments to the function
7113      * will be the args provided or the current node. If the function returns false at any point,
7114      * the cascade is stopped on that branch.
7115      * @param {Function} fn The function to call
7116      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7117      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7118      */
7119     cascade : function(fn, scope, args){
7120         if(fn.call(scope || this, args || this) !== false){
7121             var cs = this.childNodes;
7122             for(var i = 0, len = cs.length; i < len; i++) {
7123                 cs[i].cascade(fn, scope, args);
7124             }
7125         }
7126     },
7127
7128     /**
7129      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
7130      * function call will be the scope provided or the current node. The arguments to the function
7131      * will be the args provided or the current node. If the function returns false at any point,
7132      * the iteration stops.
7133      * @param {Function} fn The function to call
7134      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7135      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7136      */
7137     eachChild : function(fn, scope, args){
7138         var cs = this.childNodes;
7139         for(var i = 0, len = cs.length; i < len; i++) {
7140                 if(fn.call(scope || this, args || cs[i]) === false){
7141                     break;
7142                 }
7143         }
7144     },
7145
7146     /**
7147      * Finds the first child that has the attribute with the specified value.
7148      * @param {String} attribute The attribute name
7149      * @param {Mixed} value The value to search for
7150      * @return {Node} The found child or null if none was found
7151      */
7152     findChild : function(attribute, value){
7153         var cs = this.childNodes;
7154         for(var i = 0, len = cs.length; i < len; i++) {
7155                 if(cs[i].attributes[attribute] == value){
7156                     return cs[i];
7157                 }
7158         }
7159         return null;
7160     },
7161
7162     /**
7163      * Finds the first child by a custom function. The child matches if the function passed
7164      * returns true.
7165      * @param {Function} fn
7166      * @param {Object} scope (optional)
7167      * @return {Node} The found child or null if none was found
7168      */
7169     findChildBy : function(fn, scope){
7170         var cs = this.childNodes;
7171         for(var i = 0, len = cs.length; i < len; i++) {
7172                 if(fn.call(scope||cs[i], cs[i]) === true){
7173                     return cs[i];
7174                 }
7175         }
7176         return null;
7177     },
7178
7179     /**
7180      * Sorts this nodes children using the supplied sort function
7181      * @param {Function} fn
7182      * @param {Object} scope (optional)
7183      */
7184     sort : function(fn, scope){
7185         var cs = this.childNodes;
7186         var len = cs.length;
7187         if(len > 0){
7188             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
7189             cs.sort(sortFn);
7190             for(var i = 0; i < len; i++){
7191                 var n = cs[i];
7192                 n.previousSibling = cs[i-1];
7193                 n.nextSibling = cs[i+1];
7194                 if(i == 0){
7195                     this.setFirstChild(n);
7196                 }
7197                 if(i == len-1){
7198                     this.setLastChild(n);
7199                 }
7200             }
7201         }
7202     },
7203
7204     /**
7205      * Returns true if this node is an ancestor (at any point) of the passed node.
7206      * @param {Node} node
7207      * @return {Boolean}
7208      */
7209     contains : function(node){
7210         return node.isAncestor(this);
7211     },
7212
7213     /**
7214      * Returns true if the passed node is an ancestor (at any point) of this node.
7215      * @param {Node} node
7216      * @return {Boolean}
7217      */
7218     isAncestor : function(node){
7219         var p = this.parentNode;
7220         while(p){
7221             if(p == node){
7222                 return true;
7223             }
7224             p = p.parentNode;
7225         }
7226         return false;
7227     },
7228
7229     toString : function(){
7230         return "[Node"+(this.id?" "+this.id:"")+"]";
7231     }
7232 });/*
7233  * Based on:
7234  * Ext JS Library 1.1.1
7235  * Copyright(c) 2006-2007, Ext JS, LLC.
7236  *
7237  * Originally Released Under LGPL - original licence link has changed is not relivant.
7238  *
7239  * Fork - LGPL
7240  * <script type="text/javascript">
7241  */
7242  
7243
7244 /**
7245  * @class Roo.ComponentMgr
7246  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
7247  * @singleton
7248  */
7249 Roo.ComponentMgr = function(){
7250     var all = new Roo.util.MixedCollection();
7251
7252     return {
7253         /**
7254          * Registers a component.
7255          * @param {Roo.Component} c The component
7256          */
7257         register : function(c){
7258             all.add(c);
7259         },
7260
7261         /**
7262          * Unregisters a component.
7263          * @param {Roo.Component} c The component
7264          */
7265         unregister : function(c){
7266             all.remove(c);
7267         },
7268
7269         /**
7270          * Returns a component by id
7271          * @param {String} id The component id
7272          */
7273         get : function(id){
7274             return all.get(id);
7275         },
7276
7277         /**
7278          * Registers a function that will be called when a specified component is added to ComponentMgr
7279          * @param {String} id The component id
7280          * @param {Funtction} fn The callback function
7281          * @param {Object} scope The scope of the callback
7282          */
7283         onAvailable : function(id, fn, scope){
7284             all.on("add", function(index, o){
7285                 if(o.id == id){
7286                     fn.call(scope || o, o);
7287                     all.un("add", fn, scope);
7288                 }
7289             });
7290         }
7291     };
7292 }();/*
7293  * Based on:
7294  * Ext JS Library 1.1.1
7295  * Copyright(c) 2006-2007, Ext JS, LLC.
7296  *
7297  * Originally Released Under LGPL - original licence link has changed is not relivant.
7298  *
7299  * Fork - LGPL
7300  * <script type="text/javascript">
7301  */
7302  
7303 /**
7304  * @class Roo.Component
7305  * @extends Roo.util.Observable
7306  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
7307  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
7308  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
7309  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
7310  * All visual components (widgets) that require rendering into a layout should subclass Component.
7311  * @constructor
7312  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
7313  * 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
7314  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
7315  */
7316 Roo.Component = function(config){
7317     config = config || {};
7318     if(config.tagName || config.dom || typeof config == "string"){ // element object
7319         config = {el: config, id: config.id || config};
7320     }
7321     this.initialConfig = config;
7322
7323     Roo.apply(this, config);
7324     this.addEvents({
7325         /**
7326          * @event disable
7327          * Fires after the component is disabled.
7328              * @param {Roo.Component} this
7329              */
7330         disable : true,
7331         /**
7332          * @event enable
7333          * Fires after the component is enabled.
7334              * @param {Roo.Component} this
7335              */
7336         enable : true,
7337         /**
7338          * @event beforeshow
7339          * Fires before the component is shown.  Return false to stop the show.
7340              * @param {Roo.Component} this
7341              */
7342         beforeshow : true,
7343         /**
7344          * @event show
7345          * Fires after the component is shown.
7346              * @param {Roo.Component} this
7347              */
7348         show : true,
7349         /**
7350          * @event beforehide
7351          * Fires before the component is hidden. Return false to stop the hide.
7352              * @param {Roo.Component} this
7353              */
7354         beforehide : true,
7355         /**
7356          * @event hide
7357          * Fires after the component is hidden.
7358              * @param {Roo.Component} this
7359              */
7360         hide : true,
7361         /**
7362          * @event beforerender
7363          * Fires before the component is rendered. Return false to stop the render.
7364              * @param {Roo.Component} this
7365              */
7366         beforerender : true,
7367         /**
7368          * @event render
7369          * Fires after the component is rendered.
7370              * @param {Roo.Component} this
7371              */
7372         render : true,
7373         /**
7374          * @event beforedestroy
7375          * Fires before the component is destroyed. Return false to stop the destroy.
7376              * @param {Roo.Component} this
7377              */
7378         beforedestroy : true,
7379         /**
7380          * @event destroy
7381          * Fires after the component is destroyed.
7382              * @param {Roo.Component} this
7383              */
7384         destroy : true
7385     });
7386     if(!this.id){
7387         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
7388     }
7389     Roo.ComponentMgr.register(this);
7390     Roo.Component.superclass.constructor.call(this);
7391     this.initComponent();
7392     if(this.renderTo){ // not supported by all components yet. use at your own risk!
7393         this.render(this.renderTo);
7394         delete this.renderTo;
7395     }
7396 };
7397
7398 /** @private */
7399 Roo.Component.AUTO_ID = 1000;
7400
7401 Roo.extend(Roo.Component, Roo.util.Observable, {
7402     /**
7403      * @scope Roo.Component.prototype
7404      * @type {Boolean}
7405      * true if this component is hidden. Read-only.
7406      */
7407     hidden : false,
7408     /**
7409      * @type {Boolean}
7410      * true if this component is disabled. Read-only.
7411      */
7412     disabled : false,
7413     /**
7414      * @type {Boolean}
7415      * true if this component has been rendered. Read-only.
7416      */
7417     rendered : false,
7418     
7419     /** @cfg {String} disableClass
7420      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
7421      */
7422     disabledClass : "x-item-disabled",
7423         /** @cfg {Boolean} allowDomMove
7424          * Whether the component can move the Dom node when rendering (defaults to true).
7425          */
7426     allowDomMove : true,
7427     /** @cfg {String} hideMode
7428      * How this component should hidden. Supported values are
7429      * "visibility" (css visibility), "offsets" (negative offset position) and
7430      * "display" (css display) - defaults to "display".
7431      */
7432     hideMode: 'display',
7433
7434     /** @private */
7435     ctype : "Roo.Component",
7436
7437     /**
7438      * @cfg {String} actionMode 
7439      * which property holds the element that used for  hide() / show() / disable() / enable()
7440      * default is 'el' 
7441      */
7442     actionMode : "el",
7443
7444     /** @private */
7445     getActionEl : function(){
7446         return this[this.actionMode];
7447     },
7448
7449     initComponent : Roo.emptyFn,
7450     /**
7451      * If this is a lazy rendering component, render it to its container element.
7452      * @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.
7453      */
7454     render : function(container, position){
7455         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
7456             if(!container && this.el){
7457                 this.el = Roo.get(this.el);
7458                 container = this.el.dom.parentNode;
7459                 this.allowDomMove = false;
7460             }
7461             this.container = Roo.get(container);
7462             this.rendered = true;
7463             if(position !== undefined){
7464                 if(typeof position == 'number'){
7465                     position = this.container.dom.childNodes[position];
7466                 }else{
7467                     position = Roo.getDom(position);
7468                 }
7469             }
7470             this.onRender(this.container, position || null);
7471             if(this.cls){
7472                 this.el.addClass(this.cls);
7473                 delete this.cls;
7474             }
7475             if(this.style){
7476                 this.el.applyStyles(this.style);
7477                 delete this.style;
7478             }
7479             this.fireEvent("render", this);
7480             this.afterRender(this.container);
7481             if(this.hidden){
7482                 this.hide();
7483             }
7484             if(this.disabled){
7485                 this.disable();
7486             }
7487         }
7488         return this;
7489     },
7490
7491     /** @private */
7492     // default function is not really useful
7493     onRender : function(ct, position){
7494         if(this.el){
7495             this.el = Roo.get(this.el);
7496             if(this.allowDomMove !== false){
7497                 ct.dom.insertBefore(this.el.dom, position);
7498             }
7499         }
7500     },
7501
7502     /** @private */
7503     getAutoCreate : function(){
7504         var cfg = typeof this.autoCreate == "object" ?
7505                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
7506         if(this.id && !cfg.id){
7507             cfg.id = this.id;
7508         }
7509         return cfg;
7510     },
7511
7512     /** @private */
7513     afterRender : Roo.emptyFn,
7514
7515     /**
7516      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
7517      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
7518      */
7519     destroy : function(){
7520         if(this.fireEvent("beforedestroy", this) !== false){
7521             this.purgeListeners();
7522             this.beforeDestroy();
7523             if(this.rendered){
7524                 this.el.removeAllListeners();
7525                 this.el.remove();
7526                 if(this.actionMode == "container"){
7527                     this.container.remove();
7528                 }
7529             }
7530             this.onDestroy();
7531             Roo.ComponentMgr.unregister(this);
7532             this.fireEvent("destroy", this);
7533         }
7534     },
7535
7536         /** @private */
7537     beforeDestroy : function(){
7538
7539     },
7540
7541         /** @private */
7542         onDestroy : function(){
7543
7544     },
7545
7546     /**
7547      * Returns the underlying {@link Roo.Element}.
7548      * @return {Roo.Element} The element
7549      */
7550     getEl : function(){
7551         return this.el;
7552     },
7553
7554     /**
7555      * Returns the id of this component.
7556      * @return {String}
7557      */
7558     getId : function(){
7559         return this.id;
7560     },
7561
7562     /**
7563      * Try to focus this component.
7564      * @param {Boolean} selectText True to also select the text in this component (if applicable)
7565      * @return {Roo.Component} this
7566      */
7567     focus : function(selectText){
7568         if(this.rendered){
7569             this.el.focus();
7570             if(selectText === true){
7571                 this.el.dom.select();
7572             }
7573         }
7574         return this;
7575     },
7576
7577     /** @private */
7578     blur : function(){
7579         if(this.rendered){
7580             this.el.blur();
7581         }
7582         return this;
7583     },
7584
7585     /**
7586      * Disable this component.
7587      * @return {Roo.Component} this
7588      */
7589     disable : function(){
7590         if(this.rendered){
7591             this.onDisable();
7592         }
7593         this.disabled = true;
7594         this.fireEvent("disable", this);
7595         return this;
7596     },
7597
7598         // private
7599     onDisable : function(){
7600         this.getActionEl().addClass(this.disabledClass);
7601         this.el.dom.disabled = true;
7602     },
7603
7604     /**
7605      * Enable this component.
7606      * @return {Roo.Component} this
7607      */
7608     enable : function(){
7609         if(this.rendered){
7610             this.onEnable();
7611         }
7612         this.disabled = false;
7613         this.fireEvent("enable", this);
7614         return this;
7615     },
7616
7617         // private
7618     onEnable : function(){
7619         this.getActionEl().removeClass(this.disabledClass);
7620         this.el.dom.disabled = false;
7621     },
7622
7623     /**
7624      * Convenience function for setting disabled/enabled by boolean.
7625      * @param {Boolean} disabled
7626      */
7627     setDisabled : function(disabled){
7628         this[disabled ? "disable" : "enable"]();
7629     },
7630
7631     /**
7632      * Show this component.
7633      * @return {Roo.Component} this
7634      */
7635     show: function(){
7636         if(this.fireEvent("beforeshow", this) !== false){
7637             this.hidden = false;
7638             if(this.rendered){
7639                 this.onShow();
7640             }
7641             this.fireEvent("show", this);
7642         }
7643         return this;
7644     },
7645
7646     // private
7647     onShow : function(){
7648         var ae = this.getActionEl();
7649         if(this.hideMode == 'visibility'){
7650             ae.dom.style.visibility = "visible";
7651         }else if(this.hideMode == 'offsets'){
7652             ae.removeClass('x-hidden');
7653         }else{
7654             ae.dom.style.display = "";
7655         }
7656     },
7657
7658     /**
7659      * Hide this component.
7660      * @return {Roo.Component} this
7661      */
7662     hide: function(){
7663         if(this.fireEvent("beforehide", this) !== false){
7664             this.hidden = true;
7665             if(this.rendered){
7666                 this.onHide();
7667             }
7668             this.fireEvent("hide", this);
7669         }
7670         return this;
7671     },
7672
7673     // private
7674     onHide : function(){
7675         var ae = this.getActionEl();
7676         if(this.hideMode == 'visibility'){
7677             ae.dom.style.visibility = "hidden";
7678         }else if(this.hideMode == 'offsets'){
7679             ae.addClass('x-hidden');
7680         }else{
7681             ae.dom.style.display = "none";
7682         }
7683     },
7684
7685     /**
7686      * Convenience function to hide or show this component by boolean.
7687      * @param {Boolean} visible True to show, false to hide
7688      * @return {Roo.Component} this
7689      */
7690     setVisible: function(visible){
7691         if(visible) {
7692             this.show();
7693         }else{
7694             this.hide();
7695         }
7696         return this;
7697     },
7698
7699     /**
7700      * Returns true if this component is visible.
7701      */
7702     isVisible : function(){
7703         return this.getActionEl().isVisible();
7704     },
7705
7706     cloneConfig : function(overrides){
7707         overrides = overrides || {};
7708         var id = overrides.id || Roo.id();
7709         var cfg = Roo.applyIf(overrides, this.initialConfig);
7710         cfg.id = id; // prevent dup id
7711         return new this.constructor(cfg);
7712     }
7713 });/*
7714  * Based on:
7715  * Ext JS Library 1.1.1
7716  * Copyright(c) 2006-2007, Ext JS, LLC.
7717  *
7718  * Originally Released Under LGPL - original licence link has changed is not relivant.
7719  *
7720  * Fork - LGPL
7721  * <script type="text/javascript">
7722  */
7723  (function(){ 
7724 /**
7725  * @class Roo.Layer
7726  * @extends Roo.Element
7727  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
7728  * automatic maintaining of shadow/shim positions.
7729  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
7730  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
7731  * you can pass a string with a CSS class name. False turns off the shadow.
7732  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
7733  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
7734  * @cfg {String} cls CSS class to add to the element
7735  * @cfg {Number} zindex Starting z-index (defaults to 11000)
7736  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
7737  * @constructor
7738  * @param {Object} config An object with config options.
7739  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
7740  */
7741
7742 Roo.Layer = function(config, existingEl){
7743     config = config || {};
7744     var dh = Roo.DomHelper;
7745     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
7746     if(existingEl){
7747         this.dom = Roo.getDom(existingEl);
7748     }
7749     if(!this.dom){
7750         var o = config.dh || {tag: "div", cls: "x-layer"};
7751         this.dom = dh.append(pel, o);
7752     }
7753     if(config.cls){
7754         this.addClass(config.cls);
7755     }
7756     this.constrain = config.constrain !== false;
7757     this.visibilityMode = Roo.Element.VISIBILITY;
7758     if(config.id){
7759         this.id = this.dom.id = config.id;
7760     }else{
7761         this.id = Roo.id(this.dom);
7762     }
7763     this.zindex = config.zindex || this.getZIndex();
7764     this.position("absolute", this.zindex);
7765     if(config.shadow){
7766         this.shadowOffset = config.shadowOffset || 4;
7767         this.shadow = new Roo.Shadow({
7768             offset : this.shadowOffset,
7769             mode : config.shadow
7770         });
7771     }else{
7772         this.shadowOffset = 0;
7773     }
7774     this.useShim = config.shim !== false && Roo.useShims;
7775     this.useDisplay = config.useDisplay;
7776     this.hide();
7777 };
7778
7779 var supr = Roo.Element.prototype;
7780
7781 // shims are shared among layer to keep from having 100 iframes
7782 var shims = [];
7783
7784 Roo.extend(Roo.Layer, Roo.Element, {
7785
7786     getZIndex : function(){
7787         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
7788     },
7789
7790     getShim : function(){
7791         if(!this.useShim){
7792             return null;
7793         }
7794         if(this.shim){
7795             return this.shim;
7796         }
7797         var shim = shims.shift();
7798         if(!shim){
7799             shim = this.createShim();
7800             shim.enableDisplayMode('block');
7801             shim.dom.style.display = 'none';
7802             shim.dom.style.visibility = 'visible';
7803         }
7804         var pn = this.dom.parentNode;
7805         if(shim.dom.parentNode != pn){
7806             pn.insertBefore(shim.dom, this.dom);
7807         }
7808         shim.setStyle('z-index', this.getZIndex()-2);
7809         this.shim = shim;
7810         return shim;
7811     },
7812
7813     hideShim : function(){
7814         if(this.shim){
7815             this.shim.setDisplayed(false);
7816             shims.push(this.shim);
7817             delete this.shim;
7818         }
7819     },
7820
7821     disableShadow : function(){
7822         if(this.shadow){
7823             this.shadowDisabled = true;
7824             this.shadow.hide();
7825             this.lastShadowOffset = this.shadowOffset;
7826             this.shadowOffset = 0;
7827         }
7828     },
7829
7830     enableShadow : function(show){
7831         if(this.shadow){
7832             this.shadowDisabled = false;
7833             this.shadowOffset = this.lastShadowOffset;
7834             delete this.lastShadowOffset;
7835             if(show){
7836                 this.sync(true);
7837             }
7838         }
7839     },
7840
7841     // private
7842     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
7843     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
7844     sync : function(doShow){
7845         var sw = this.shadow;
7846         if(!this.updating && this.isVisible() && (sw || this.useShim)){
7847             var sh = this.getShim();
7848
7849             var w = this.getWidth(),
7850                 h = this.getHeight();
7851
7852             var l = this.getLeft(true),
7853                 t = this.getTop(true);
7854
7855             if(sw && !this.shadowDisabled){
7856                 if(doShow && !sw.isVisible()){
7857                     sw.show(this);
7858                 }else{
7859                     sw.realign(l, t, w, h);
7860                 }
7861                 if(sh){
7862                     if(doShow){
7863                        sh.show();
7864                     }
7865                     // fit the shim behind the shadow, so it is shimmed too
7866                     var a = sw.adjusts, s = sh.dom.style;
7867                     s.left = (Math.min(l, l+a.l))+"px";
7868                     s.top = (Math.min(t, t+a.t))+"px";
7869                     s.width = (w+a.w)+"px";
7870                     s.height = (h+a.h)+"px";
7871                 }
7872             }else if(sh){
7873                 if(doShow){
7874                    sh.show();
7875                 }
7876                 sh.setSize(w, h);
7877                 sh.setLeftTop(l, t);
7878             }
7879             
7880         }
7881     },
7882
7883     // private
7884     destroy : function(){
7885         this.hideShim();
7886         if(this.shadow){
7887             this.shadow.hide();
7888         }
7889         this.removeAllListeners();
7890         var pn = this.dom.parentNode;
7891         if(pn){
7892             pn.removeChild(this.dom);
7893         }
7894         Roo.Element.uncache(this.id);
7895     },
7896
7897     remove : function(){
7898         this.destroy();
7899     },
7900
7901     // private
7902     beginUpdate : function(){
7903         this.updating = true;
7904     },
7905
7906     // private
7907     endUpdate : function(){
7908         this.updating = false;
7909         this.sync(true);
7910     },
7911
7912     // private
7913     hideUnders : function(negOffset){
7914         if(this.shadow){
7915             this.shadow.hide();
7916         }
7917         this.hideShim();
7918     },
7919
7920     // private
7921     constrainXY : function(){
7922         if(this.constrain){
7923             var vw = Roo.lib.Dom.getViewWidth(),
7924                 vh = Roo.lib.Dom.getViewHeight();
7925             var s = Roo.get(document).getScroll();
7926
7927             var xy = this.getXY();
7928             var x = xy[0], y = xy[1];   
7929             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
7930             // only move it if it needs it
7931             var moved = false;
7932             // first validate right/bottom
7933             if((x + w) > vw+s.left){
7934                 x = vw - w - this.shadowOffset;
7935                 moved = true;
7936             }
7937             if((y + h) > vh+s.top){
7938                 y = vh - h - this.shadowOffset;
7939                 moved = true;
7940             }
7941             // then make sure top/left isn't negative
7942             if(x < s.left){
7943                 x = s.left;
7944                 moved = true;
7945             }
7946             if(y < s.top){
7947                 y = s.top;
7948                 moved = true;
7949             }
7950             if(moved){
7951                 if(this.avoidY){
7952                     var ay = this.avoidY;
7953                     if(y <= ay && (y+h) >= ay){
7954                         y = ay-h-5;   
7955                     }
7956                 }
7957                 xy = [x, y];
7958                 this.storeXY(xy);
7959                 supr.setXY.call(this, xy);
7960                 this.sync();
7961             }
7962         }
7963     },
7964
7965     isVisible : function(){
7966         return this.visible;    
7967     },
7968
7969     // private
7970     showAction : function(){
7971         this.visible = true; // track visibility to prevent getStyle calls
7972         if(this.useDisplay === true){
7973             this.setDisplayed("");
7974         }else if(this.lastXY){
7975             supr.setXY.call(this, this.lastXY);
7976         }else if(this.lastLT){
7977             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
7978         }
7979     },
7980
7981     // private
7982     hideAction : function(){
7983         this.visible = false;
7984         if(this.useDisplay === true){
7985             this.setDisplayed(false);
7986         }else{
7987             this.setLeftTop(-10000,-10000);
7988         }
7989     },
7990
7991     // overridden Element method
7992     setVisible : function(v, a, d, c, e){
7993         if(v){
7994             this.showAction();
7995         }
7996         if(a && v){
7997             var cb = function(){
7998                 this.sync(true);
7999                 if(c){
8000                     c();
8001                 }
8002             }.createDelegate(this);
8003             supr.setVisible.call(this, true, true, d, cb, e);
8004         }else{
8005             if(!v){
8006                 this.hideUnders(true);
8007             }
8008             var cb = c;
8009             if(a){
8010                 cb = function(){
8011                     this.hideAction();
8012                     if(c){
8013                         c();
8014                     }
8015                 }.createDelegate(this);
8016             }
8017             supr.setVisible.call(this, v, a, d, cb, e);
8018             if(v){
8019                 this.sync(true);
8020             }else if(!a){
8021                 this.hideAction();
8022             }
8023         }
8024     },
8025
8026     storeXY : function(xy){
8027         delete this.lastLT;
8028         this.lastXY = xy;
8029     },
8030
8031     storeLeftTop : function(left, top){
8032         delete this.lastXY;
8033         this.lastLT = [left, top];
8034     },
8035
8036     // private
8037     beforeFx : function(){
8038         this.beforeAction();
8039         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
8040     },
8041
8042     // private
8043     afterFx : function(){
8044         Roo.Layer.superclass.afterFx.apply(this, arguments);
8045         this.sync(this.isVisible());
8046     },
8047
8048     // private
8049     beforeAction : function(){
8050         if(!this.updating && this.shadow){
8051             this.shadow.hide();
8052         }
8053     },
8054
8055     // overridden Element method
8056     setLeft : function(left){
8057         this.storeLeftTop(left, this.getTop(true));
8058         supr.setLeft.apply(this, arguments);
8059         this.sync();
8060     },
8061
8062     setTop : function(top){
8063         this.storeLeftTop(this.getLeft(true), top);
8064         supr.setTop.apply(this, arguments);
8065         this.sync();
8066     },
8067
8068     setLeftTop : function(left, top){
8069         this.storeLeftTop(left, top);
8070         supr.setLeftTop.apply(this, arguments);
8071         this.sync();
8072     },
8073
8074     setXY : function(xy, a, d, c, e){
8075         this.fixDisplay();
8076         this.beforeAction();
8077         this.storeXY(xy);
8078         var cb = this.createCB(c);
8079         supr.setXY.call(this, xy, a, d, cb, e);
8080         if(!a){
8081             cb();
8082         }
8083     },
8084
8085     // private
8086     createCB : function(c){
8087         var el = this;
8088         return function(){
8089             el.constrainXY();
8090             el.sync(true);
8091             if(c){
8092                 c();
8093             }
8094         };
8095     },
8096
8097     // overridden Element method
8098     setX : function(x, a, d, c, e){
8099         this.setXY([x, this.getY()], a, d, c, e);
8100     },
8101
8102     // overridden Element method
8103     setY : function(y, a, d, c, e){
8104         this.setXY([this.getX(), y], a, d, c, e);
8105     },
8106
8107     // overridden Element method
8108     setSize : function(w, h, a, d, c, e){
8109         this.beforeAction();
8110         var cb = this.createCB(c);
8111         supr.setSize.call(this, w, h, a, d, cb, e);
8112         if(!a){
8113             cb();
8114         }
8115     },
8116
8117     // overridden Element method
8118     setWidth : function(w, a, d, c, e){
8119         this.beforeAction();
8120         var cb = this.createCB(c);
8121         supr.setWidth.call(this, w, a, d, cb, e);
8122         if(!a){
8123             cb();
8124         }
8125     },
8126
8127     // overridden Element method
8128     setHeight : function(h, a, d, c, e){
8129         this.beforeAction();
8130         var cb = this.createCB(c);
8131         supr.setHeight.call(this, h, a, d, cb, e);
8132         if(!a){
8133             cb();
8134         }
8135     },
8136
8137     // overridden Element method
8138     setBounds : function(x, y, w, h, a, d, c, e){
8139         this.beforeAction();
8140         var cb = this.createCB(c);
8141         if(!a){
8142             this.storeXY([x, y]);
8143             supr.setXY.call(this, [x, y]);
8144             supr.setSize.call(this, w, h, a, d, cb, e);
8145             cb();
8146         }else{
8147             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
8148         }
8149         return this;
8150     },
8151     
8152     /**
8153      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
8154      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
8155      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
8156      * @param {Number} zindex The new z-index to set
8157      * @return {this} The Layer
8158      */
8159     setZIndex : function(zindex){
8160         this.zindex = zindex;
8161         this.setStyle("z-index", zindex + 2);
8162         if(this.shadow){
8163             this.shadow.setZIndex(zindex + 1);
8164         }
8165         if(this.shim){
8166             this.shim.setStyle("z-index", zindex);
8167         }
8168     }
8169 });
8170 })();/*
8171  * Based on:
8172  * Ext JS Library 1.1.1
8173  * Copyright(c) 2006-2007, Ext JS, LLC.
8174  *
8175  * Originally Released Under LGPL - original licence link has changed is not relivant.
8176  *
8177  * Fork - LGPL
8178  * <script type="text/javascript">
8179  */
8180
8181
8182 /**
8183  * @class Roo.Shadow
8184  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
8185  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
8186  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
8187  * @constructor
8188  * Create a new Shadow
8189  * @param {Object} config The config object
8190  */
8191 Roo.Shadow = function(config){
8192     Roo.apply(this, config);
8193     if(typeof this.mode != "string"){
8194         this.mode = this.defaultMode;
8195     }
8196     var o = this.offset, a = {h: 0};
8197     var rad = Math.floor(this.offset/2);
8198     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
8199         case "drop":
8200             a.w = 0;
8201             a.l = a.t = o;
8202             a.t -= 1;
8203             if(Roo.isIE){
8204                 a.l -= this.offset + rad;
8205                 a.t -= this.offset + rad;
8206                 a.w -= rad;
8207                 a.h -= rad;
8208                 a.t += 1;
8209             }
8210         break;
8211         case "sides":
8212             a.w = (o*2);
8213             a.l = -o;
8214             a.t = o-1;
8215             if(Roo.isIE){
8216                 a.l -= (this.offset - rad);
8217                 a.t -= this.offset + rad;
8218                 a.l += 1;
8219                 a.w -= (this.offset - rad)*2;
8220                 a.w -= rad + 1;
8221                 a.h -= 1;
8222             }
8223         break;
8224         case "frame":
8225             a.w = a.h = (o*2);
8226             a.l = a.t = -o;
8227             a.t += 1;
8228             a.h -= 2;
8229             if(Roo.isIE){
8230                 a.l -= (this.offset - rad);
8231                 a.t -= (this.offset - rad);
8232                 a.l += 1;
8233                 a.w -= (this.offset + rad + 1);
8234                 a.h -= (this.offset + rad);
8235                 a.h += 1;
8236             }
8237         break;
8238     };
8239
8240     this.adjusts = a;
8241 };
8242
8243 Roo.Shadow.prototype = {
8244     /**
8245      * @cfg {String} mode
8246      * The shadow display mode.  Supports the following options:<br />
8247      * sides: Shadow displays on both sides and bottom only<br />
8248      * frame: Shadow displays equally on all four sides<br />
8249      * drop: Traditional bottom-right drop shadow (default)
8250      */
8251     /**
8252      * @cfg {String} offset
8253      * The number of pixels to offset the shadow from the element (defaults to 4)
8254      */
8255     offset: 4,
8256
8257     // private
8258     defaultMode: "drop",
8259
8260     /**
8261      * Displays the shadow under the target element
8262      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
8263      */
8264     show : function(target){
8265         target = Roo.get(target);
8266         if(!this.el){
8267             this.el = Roo.Shadow.Pool.pull();
8268             if(this.el.dom.nextSibling != target.dom){
8269                 this.el.insertBefore(target);
8270             }
8271         }
8272         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
8273         if(Roo.isIE){
8274             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
8275         }
8276         this.realign(
8277             target.getLeft(true),
8278             target.getTop(true),
8279             target.getWidth(),
8280             target.getHeight()
8281         );
8282         this.el.dom.style.display = "block";
8283     },
8284
8285     /**
8286      * Returns true if the shadow is visible, else false
8287      */
8288     isVisible : function(){
8289         return this.el ? true : false;  
8290     },
8291
8292     /**
8293      * Direct alignment when values are already available. Show must be called at least once before
8294      * calling this method to ensure it is initialized.
8295      * @param {Number} left The target element left position
8296      * @param {Number} top The target element top position
8297      * @param {Number} width The target element width
8298      * @param {Number} height The target element height
8299      */
8300     realign : function(l, t, w, h){
8301         if(!this.el){
8302             return;
8303         }
8304         var a = this.adjusts, d = this.el.dom, s = d.style;
8305         var iea = 0;
8306         s.left = (l+a.l)+"px";
8307         s.top = (t+a.t)+"px";
8308         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
8309  
8310         if(s.width != sws || s.height != shs){
8311             s.width = sws;
8312             s.height = shs;
8313             if(!Roo.isIE){
8314                 var cn = d.childNodes;
8315                 var sww = Math.max(0, (sw-12))+"px";
8316                 cn[0].childNodes[1].style.width = sww;
8317                 cn[1].childNodes[1].style.width = sww;
8318                 cn[2].childNodes[1].style.width = sww;
8319                 cn[1].style.height = Math.max(0, (sh-12))+"px";
8320             }
8321         }
8322     },
8323
8324     /**
8325      * Hides this shadow
8326      */
8327     hide : function(){
8328         if(this.el){
8329             this.el.dom.style.display = "none";
8330             Roo.Shadow.Pool.push(this.el);
8331             delete this.el;
8332         }
8333     },
8334
8335     /**
8336      * Adjust the z-index of this shadow
8337      * @param {Number} zindex The new z-index
8338      */
8339     setZIndex : function(z){
8340         this.zIndex = z;
8341         if(this.el){
8342             this.el.setStyle("z-index", z);
8343         }
8344     }
8345 };
8346
8347 // Private utility class that manages the internal Shadow cache
8348 Roo.Shadow.Pool = function(){
8349     var p = [];
8350     var markup = Roo.isIE ?
8351                  '<div class="x-ie-shadow"></div>' :
8352                  '<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>';
8353     return {
8354         pull : function(){
8355             var sh = p.shift();
8356             if(!sh){
8357                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
8358                 sh.autoBoxAdjust = false;
8359             }
8360             return sh;
8361         },
8362
8363         push : function(sh){
8364             p.push(sh);
8365         }
8366     };
8367 }();/*
8368  * Based on:
8369  * Ext JS Library 1.1.1
8370  * Copyright(c) 2006-2007, Ext JS, LLC.
8371  *
8372  * Originally Released Under LGPL - original licence link has changed is not relivant.
8373  *
8374  * Fork - LGPL
8375  * <script type="text/javascript">
8376  */
8377
8378 /**
8379  * @class Roo.BoxComponent
8380  * @extends Roo.Component
8381  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
8382  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
8383  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
8384  * layout containers.
8385  * @constructor
8386  * @param {Roo.Element/String/Object} config The configuration options.
8387  */
8388 Roo.BoxComponent = function(config){
8389     Roo.Component.call(this, config);
8390     this.addEvents({
8391         /**
8392          * @event resize
8393          * Fires after the component is resized.
8394              * @param {Roo.Component} this
8395              * @param {Number} adjWidth The box-adjusted width that was set
8396              * @param {Number} adjHeight The box-adjusted height that was set
8397              * @param {Number} rawWidth The width that was originally specified
8398              * @param {Number} rawHeight The height that was originally specified
8399              */
8400         resize : true,
8401         /**
8402          * @event move
8403          * Fires after the component is moved.
8404              * @param {Roo.Component} this
8405              * @param {Number} x The new x position
8406              * @param {Number} y The new y position
8407              */
8408         move : true
8409     });
8410 };
8411
8412 Roo.extend(Roo.BoxComponent, Roo.Component, {
8413     // private, set in afterRender to signify that the component has been rendered
8414     boxReady : false,
8415     // private, used to defer height settings to subclasses
8416     deferHeight: false,
8417     /** @cfg {Number} width
8418      * width (optional) size of component
8419      */
8420      /** @cfg {Number} height
8421      * height (optional) size of component
8422      */
8423      
8424     /**
8425      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
8426      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
8427      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
8428      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
8429      * @return {Roo.BoxComponent} this
8430      */
8431     setSize : function(w, h){
8432         // support for standard size objects
8433         if(typeof w == 'object'){
8434             h = w.height;
8435             w = w.width;
8436         }
8437         // not rendered
8438         if(!this.boxReady){
8439             this.width = w;
8440             this.height = h;
8441             return this;
8442         }
8443
8444         // prevent recalcs when not needed
8445         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
8446             return this;
8447         }
8448         this.lastSize = {width: w, height: h};
8449
8450         var adj = this.adjustSize(w, h);
8451         var aw = adj.width, ah = adj.height;
8452         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
8453             var rz = this.getResizeEl();
8454             if(!this.deferHeight && aw !== undefined && ah !== undefined){
8455                 rz.setSize(aw, ah);
8456             }else if(!this.deferHeight && ah !== undefined){
8457                 rz.setHeight(ah);
8458             }else if(aw !== undefined){
8459                 rz.setWidth(aw);
8460             }
8461             this.onResize(aw, ah, w, h);
8462             this.fireEvent('resize', this, aw, ah, w, h);
8463         }
8464         return this;
8465     },
8466
8467     /**
8468      * Gets the current size of the component's underlying element.
8469      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8470      */
8471     getSize : function(){
8472         return this.el.getSize();
8473     },
8474
8475     /**
8476      * Gets the current XY position of the component's underlying element.
8477      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8478      * @return {Array} The XY position of the element (e.g., [100, 200])
8479      */
8480     getPosition : function(local){
8481         if(local === true){
8482             return [this.el.getLeft(true), this.el.getTop(true)];
8483         }
8484         return this.xy || this.el.getXY();
8485     },
8486
8487     /**
8488      * Gets the current box measurements of the component's underlying element.
8489      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8490      * @returns {Object} box An object in the format {x, y, width, height}
8491      */
8492     getBox : function(local){
8493         var s = this.el.getSize();
8494         if(local){
8495             s.x = this.el.getLeft(true);
8496             s.y = this.el.getTop(true);
8497         }else{
8498             var xy = this.xy || this.el.getXY();
8499             s.x = xy[0];
8500             s.y = xy[1];
8501         }
8502         return s;
8503     },
8504
8505     /**
8506      * Sets the current box measurements of the component's underlying element.
8507      * @param {Object} box An object in the format {x, y, width, height}
8508      * @returns {Roo.BoxComponent} this
8509      */
8510     updateBox : function(box){
8511         this.setSize(box.width, box.height);
8512         this.setPagePosition(box.x, box.y);
8513         return this;
8514     },
8515
8516     // protected
8517     getResizeEl : function(){
8518         return this.resizeEl || this.el;
8519     },
8520
8521     // protected
8522     getPositionEl : function(){
8523         return this.positionEl || this.el;
8524     },
8525
8526     /**
8527      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
8528      * This method fires the move event.
8529      * @param {Number} left The new left
8530      * @param {Number} top The new top
8531      * @returns {Roo.BoxComponent} this
8532      */
8533     setPosition : function(x, y){
8534         this.x = x;
8535         this.y = y;
8536         if(!this.boxReady){
8537             return this;
8538         }
8539         var adj = this.adjustPosition(x, y);
8540         var ax = adj.x, ay = adj.y;
8541
8542         var el = this.getPositionEl();
8543         if(ax !== undefined || ay !== undefined){
8544             if(ax !== undefined && ay !== undefined){
8545                 el.setLeftTop(ax, ay);
8546             }else if(ax !== undefined){
8547                 el.setLeft(ax);
8548             }else if(ay !== undefined){
8549                 el.setTop(ay);
8550             }
8551             this.onPosition(ax, ay);
8552             this.fireEvent('move', this, ax, ay);
8553         }
8554         return this;
8555     },
8556
8557     /**
8558      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
8559      * This method fires the move event.
8560      * @param {Number} x The new x position
8561      * @param {Number} y The new y position
8562      * @returns {Roo.BoxComponent} this
8563      */
8564     setPagePosition : function(x, y){
8565         this.pageX = x;
8566         this.pageY = y;
8567         if(!this.boxReady){
8568             return;
8569         }
8570         if(x === undefined || y === undefined){ // cannot translate undefined points
8571             return;
8572         }
8573         var p = this.el.translatePoints(x, y);
8574         this.setPosition(p.left, p.top);
8575         return this;
8576     },
8577
8578     // private
8579     onRender : function(ct, position){
8580         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
8581         if(this.resizeEl){
8582             this.resizeEl = Roo.get(this.resizeEl);
8583         }
8584         if(this.positionEl){
8585             this.positionEl = Roo.get(this.positionEl);
8586         }
8587     },
8588
8589     // private
8590     afterRender : function(){
8591         Roo.BoxComponent.superclass.afterRender.call(this);
8592         this.boxReady = true;
8593         this.setSize(this.width, this.height);
8594         if(this.x || this.y){
8595             this.setPosition(this.x, this.y);
8596         }
8597         if(this.pageX || this.pageY){
8598             this.setPagePosition(this.pageX, this.pageY);
8599         }
8600     },
8601
8602     /**
8603      * Force the component's size to recalculate based on the underlying element's current height and width.
8604      * @returns {Roo.BoxComponent} this
8605      */
8606     syncSize : function(){
8607         delete this.lastSize;
8608         this.setSize(this.el.getWidth(), this.el.getHeight());
8609         return this;
8610     },
8611
8612     /**
8613      * Called after the component is resized, this method is empty by default but can be implemented by any
8614      * subclass that needs to perform custom logic after a resize occurs.
8615      * @param {Number} adjWidth The box-adjusted width that was set
8616      * @param {Number} adjHeight The box-adjusted height that was set
8617      * @param {Number} rawWidth The width that was originally specified
8618      * @param {Number} rawHeight The height that was originally specified
8619      */
8620     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
8621
8622     },
8623
8624     /**
8625      * Called after the component is moved, this method is empty by default but can be implemented by any
8626      * subclass that needs to perform custom logic after a move occurs.
8627      * @param {Number} x The new x position
8628      * @param {Number} y The new y position
8629      */
8630     onPosition : function(x, y){
8631
8632     },
8633
8634     // private
8635     adjustSize : function(w, h){
8636         if(this.autoWidth){
8637             w = 'auto';
8638         }
8639         if(this.autoHeight){
8640             h = 'auto';
8641         }
8642         return {width : w, height: h};
8643     },
8644
8645     // private
8646     adjustPosition : function(x, y){
8647         return {x : x, y: y};
8648     }
8649 });/*
8650  * Based on:
8651  * Ext JS Library 1.1.1
8652  * Copyright(c) 2006-2007, Ext JS, LLC.
8653  *
8654  * Originally Released Under LGPL - original licence link has changed is not relivant.
8655  *
8656  * Fork - LGPL
8657  * <script type="text/javascript">
8658  */
8659
8660
8661 /**
8662  * @class Roo.SplitBar
8663  * @extends Roo.util.Observable
8664  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
8665  * <br><br>
8666  * Usage:
8667  * <pre><code>
8668 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
8669                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
8670 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
8671 split.minSize = 100;
8672 split.maxSize = 600;
8673 split.animate = true;
8674 split.on('moved', splitterMoved);
8675 </code></pre>
8676  * @constructor
8677  * Create a new SplitBar
8678  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
8679  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
8680  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8681  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
8682                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
8683                         position of the SplitBar).
8684  */
8685 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
8686     
8687     /** @private */
8688     this.el = Roo.get(dragElement, true);
8689     this.el.dom.unselectable = "on";
8690     /** @private */
8691     this.resizingEl = Roo.get(resizingElement, true);
8692
8693     /**
8694      * @private
8695      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8696      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
8697      * @type Number
8698      */
8699     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
8700     
8701     /**
8702      * The minimum size of the resizing element. (Defaults to 0)
8703      * @type Number
8704      */
8705     this.minSize = 0;
8706     
8707     /**
8708      * The maximum size of the resizing element. (Defaults to 2000)
8709      * @type Number
8710      */
8711     this.maxSize = 2000;
8712     
8713     /**
8714      * Whether to animate the transition to the new size
8715      * @type Boolean
8716      */
8717     this.animate = false;
8718     
8719     /**
8720      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
8721      * @type Boolean
8722      */
8723     this.useShim = false;
8724     
8725     /** @private */
8726     this.shim = null;
8727     
8728     if(!existingProxy){
8729         /** @private */
8730         this.proxy = Roo.SplitBar.createProxy(this.orientation);
8731     }else{
8732         this.proxy = Roo.get(existingProxy).dom;
8733     }
8734     /** @private */
8735     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
8736     
8737     /** @private */
8738     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
8739     
8740     /** @private */
8741     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
8742     
8743     /** @private */
8744     this.dragSpecs = {};
8745     
8746     /**
8747      * @private The adapter to use to positon and resize elements
8748      */
8749     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
8750     this.adapter.init(this);
8751     
8752     if(this.orientation == Roo.SplitBar.HORIZONTAL){
8753         /** @private */
8754         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
8755         this.el.addClass("x-splitbar-h");
8756     }else{
8757         /** @private */
8758         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
8759         this.el.addClass("x-splitbar-v");
8760     }
8761     
8762     this.addEvents({
8763         /**
8764          * @event resize
8765          * Fires when the splitter is moved (alias for {@link #event-moved})
8766          * @param {Roo.SplitBar} this
8767          * @param {Number} newSize the new width or height
8768          */
8769         "resize" : true,
8770         /**
8771          * @event moved
8772          * Fires when the splitter is moved
8773          * @param {Roo.SplitBar} this
8774          * @param {Number} newSize the new width or height
8775          */
8776         "moved" : true,
8777         /**
8778          * @event beforeresize
8779          * Fires before the splitter is dragged
8780          * @param {Roo.SplitBar} this
8781          */
8782         "beforeresize" : true,
8783
8784         "beforeapply" : true
8785     });
8786
8787     Roo.util.Observable.call(this);
8788 };
8789
8790 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
8791     onStartProxyDrag : function(x, y){
8792         this.fireEvent("beforeresize", this);
8793         if(!this.overlay){
8794             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
8795             o.unselectable();
8796             o.enableDisplayMode("block");
8797             // all splitbars share the same overlay
8798             Roo.SplitBar.prototype.overlay = o;
8799         }
8800         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8801         this.overlay.show();
8802         Roo.get(this.proxy).setDisplayed("block");
8803         var size = this.adapter.getElementSize(this);
8804         this.activeMinSize = this.getMinimumSize();;
8805         this.activeMaxSize = this.getMaximumSize();;
8806         var c1 = size - this.activeMinSize;
8807         var c2 = Math.max(this.activeMaxSize - size, 0);
8808         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8809             this.dd.resetConstraints();
8810             this.dd.setXConstraint(
8811                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
8812                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
8813             );
8814             this.dd.setYConstraint(0, 0);
8815         }else{
8816             this.dd.resetConstraints();
8817             this.dd.setXConstraint(0, 0);
8818             this.dd.setYConstraint(
8819                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
8820                 this.placement == Roo.SplitBar.TOP ? c2 : c1
8821             );
8822          }
8823         this.dragSpecs.startSize = size;
8824         this.dragSpecs.startPoint = [x, y];
8825         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
8826     },
8827     
8828     /** 
8829      * @private Called after the drag operation by the DDProxy
8830      */
8831     onEndProxyDrag : function(e){
8832         Roo.get(this.proxy).setDisplayed(false);
8833         var endPoint = Roo.lib.Event.getXY(e);
8834         if(this.overlay){
8835             this.overlay.hide();
8836         }
8837         var newSize;
8838         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8839             newSize = this.dragSpecs.startSize + 
8840                 (this.placement == Roo.SplitBar.LEFT ?
8841                     endPoint[0] - this.dragSpecs.startPoint[0] :
8842                     this.dragSpecs.startPoint[0] - endPoint[0]
8843                 );
8844         }else{
8845             newSize = this.dragSpecs.startSize + 
8846                 (this.placement == Roo.SplitBar.TOP ?
8847                     endPoint[1] - this.dragSpecs.startPoint[1] :
8848                     this.dragSpecs.startPoint[1] - endPoint[1]
8849                 );
8850         }
8851         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
8852         if(newSize != this.dragSpecs.startSize){
8853             if(this.fireEvent('beforeapply', this, newSize) !== false){
8854                 this.adapter.setElementSize(this, newSize);
8855                 this.fireEvent("moved", this, newSize);
8856                 this.fireEvent("resize", this, newSize);
8857             }
8858         }
8859     },
8860     
8861     /**
8862      * Get the adapter this SplitBar uses
8863      * @return The adapter object
8864      */
8865     getAdapter : function(){
8866         return this.adapter;
8867     },
8868     
8869     /**
8870      * Set the adapter this SplitBar uses
8871      * @param {Object} adapter A SplitBar adapter object
8872      */
8873     setAdapter : function(adapter){
8874         this.adapter = adapter;
8875         this.adapter.init(this);
8876     },
8877     
8878     /**
8879      * Gets the minimum size for the resizing element
8880      * @return {Number} The minimum size
8881      */
8882     getMinimumSize : function(){
8883         return this.minSize;
8884     },
8885     
8886     /**
8887      * Sets the minimum size for the resizing element
8888      * @param {Number} minSize The minimum size
8889      */
8890     setMinimumSize : function(minSize){
8891         this.minSize = minSize;
8892     },
8893     
8894     /**
8895      * Gets the maximum size for the resizing element
8896      * @return {Number} The maximum size
8897      */
8898     getMaximumSize : function(){
8899         return this.maxSize;
8900     },
8901     
8902     /**
8903      * Sets the maximum size for the resizing element
8904      * @param {Number} maxSize The maximum size
8905      */
8906     setMaximumSize : function(maxSize){
8907         this.maxSize = maxSize;
8908     },
8909     
8910     /**
8911      * Sets the initialize size for the resizing element
8912      * @param {Number} size The initial size
8913      */
8914     setCurrentSize : function(size){
8915         var oldAnimate = this.animate;
8916         this.animate = false;
8917         this.adapter.setElementSize(this, size);
8918         this.animate = oldAnimate;
8919     },
8920     
8921     /**
8922      * Destroy this splitbar. 
8923      * @param {Boolean} removeEl True to remove the element
8924      */
8925     destroy : function(removeEl){
8926         if(this.shim){
8927             this.shim.remove();
8928         }
8929         this.dd.unreg();
8930         this.proxy.parentNode.removeChild(this.proxy);
8931         if(removeEl){
8932             this.el.remove();
8933         }
8934     }
8935 });
8936
8937 /**
8938  * @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.
8939  */
8940 Roo.SplitBar.createProxy = function(dir){
8941     var proxy = new Roo.Element(document.createElement("div"));
8942     proxy.unselectable();
8943     var cls = 'x-splitbar-proxy';
8944     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
8945     document.body.appendChild(proxy.dom);
8946     return proxy.dom;
8947 };
8948
8949 /** 
8950  * @class Roo.SplitBar.BasicLayoutAdapter
8951  * Default Adapter. It assumes the splitter and resizing element are not positioned
8952  * elements and only gets/sets the width of the element. Generally used for table based layouts.
8953  */
8954 Roo.SplitBar.BasicLayoutAdapter = function(){
8955 };
8956
8957 Roo.SplitBar.BasicLayoutAdapter.prototype = {
8958     // do nothing for now
8959     init : function(s){
8960     
8961     },
8962     /**
8963      * Called before drag operations to get the current size of the resizing element. 
8964      * @param {Roo.SplitBar} s The SplitBar using this adapter
8965      */
8966      getElementSize : function(s){
8967         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8968             return s.resizingEl.getWidth();
8969         }else{
8970             return s.resizingEl.getHeight();
8971         }
8972     },
8973     
8974     /**
8975      * Called after drag operations to set the size of the resizing element.
8976      * @param {Roo.SplitBar} s The SplitBar using this adapter
8977      * @param {Number} newSize The new size to set
8978      * @param {Function} onComplete A function to be invoked when resizing is complete
8979      */
8980     setElementSize : function(s, newSize, onComplete){
8981         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8982             if(!s.animate){
8983                 s.resizingEl.setWidth(newSize);
8984                 if(onComplete){
8985                     onComplete(s, newSize);
8986                 }
8987             }else{
8988                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
8989             }
8990         }else{
8991             
8992             if(!s.animate){
8993                 s.resizingEl.setHeight(newSize);
8994                 if(onComplete){
8995                     onComplete(s, newSize);
8996                 }
8997             }else{
8998                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
8999             }
9000         }
9001     }
9002 };
9003
9004 /** 
9005  *@class Roo.SplitBar.AbsoluteLayoutAdapter
9006  * @extends Roo.SplitBar.BasicLayoutAdapter
9007  * Adapter that  moves the splitter element to align with the resized sizing element. 
9008  * Used with an absolute positioned SplitBar.
9009  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
9010  * document.body, make sure you assign an id to the body element.
9011  */
9012 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
9013     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
9014     this.container = Roo.get(container);
9015 };
9016
9017 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
9018     init : function(s){
9019         this.basic.init(s);
9020     },
9021     
9022     getElementSize : function(s){
9023         return this.basic.getElementSize(s);
9024     },
9025     
9026     setElementSize : function(s, newSize, onComplete){
9027         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
9028     },
9029     
9030     moveSplitter : function(s){
9031         var yes = Roo.SplitBar;
9032         switch(s.placement){
9033             case yes.LEFT:
9034                 s.el.setX(s.resizingEl.getRight());
9035                 break;
9036             case yes.RIGHT:
9037                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
9038                 break;
9039             case yes.TOP:
9040                 s.el.setY(s.resizingEl.getBottom());
9041                 break;
9042             case yes.BOTTOM:
9043                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
9044                 break;
9045         }
9046     }
9047 };
9048
9049 /**
9050  * Orientation constant - Create a vertical SplitBar
9051  * @static
9052  * @type Number
9053  */
9054 Roo.SplitBar.VERTICAL = 1;
9055
9056 /**
9057  * Orientation constant - Create a horizontal SplitBar
9058  * @static
9059  * @type Number
9060  */
9061 Roo.SplitBar.HORIZONTAL = 2;
9062
9063 /**
9064  * Placement constant - The resizing element is to the left of the splitter element
9065  * @static
9066  * @type Number
9067  */
9068 Roo.SplitBar.LEFT = 1;
9069
9070 /**
9071  * Placement constant - The resizing element is to the right of the splitter element
9072  * @static
9073  * @type Number
9074  */
9075 Roo.SplitBar.RIGHT = 2;
9076
9077 /**
9078  * Placement constant - The resizing element is positioned above the splitter element
9079  * @static
9080  * @type Number
9081  */
9082 Roo.SplitBar.TOP = 3;
9083
9084 /**
9085  * Placement constant - The resizing element is positioned under splitter element
9086  * @static
9087  * @type Number
9088  */
9089 Roo.SplitBar.BOTTOM = 4;
9090 /*
9091  * Based on:
9092  * Ext JS Library 1.1.1
9093  * Copyright(c) 2006-2007, Ext JS, LLC.
9094  *
9095  * Originally Released Under LGPL - original licence link has changed is not relivant.
9096  *
9097  * Fork - LGPL
9098  * <script type="text/javascript">
9099  */
9100
9101 /**
9102  * @class Roo.View
9103  * @extends Roo.util.Observable
9104  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
9105  * This class also supports single and multi selection modes. <br>
9106  * Create a data model bound view:
9107  <pre><code>
9108  var store = new Roo.data.Store(...);
9109
9110  var view = new Roo.View({
9111     el : "my-element",
9112     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
9113  
9114     singleSelect: true,
9115     selectedClass: "ydataview-selected",
9116     store: store
9117  });
9118
9119  // listen for node click?
9120  view.on("click", function(vw, index, node, e){
9121  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9122  });
9123
9124  // load XML data
9125  dataModel.load("foobar.xml");
9126  </code></pre>
9127  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9128  * <br><br>
9129  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9130  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9131  * 
9132  * Note: old style constructor is still suported (container, template, config)
9133  * 
9134  * @constructor
9135  * Create a new View
9136  * @param {Object} config The config object
9137  * 
9138  */
9139 Roo.View = function(config, depreciated_tpl, depreciated_config){
9140     
9141     if (typeof(depreciated_tpl) == 'undefined') {
9142         // new way.. - universal constructor.
9143         Roo.apply(this, config);
9144         this.el  = Roo.get(this.el);
9145     } else {
9146         // old format..
9147         this.el  = Roo.get(config);
9148         this.tpl = depreciated_tpl;
9149         Roo.apply(this, depreciated_config);
9150     }
9151      
9152     
9153     if(typeof(this.tpl) == "string"){
9154         this.tpl = new Roo.Template(this.tpl);
9155     } else {
9156         // support xtype ctors..
9157         this.tpl = new Roo.factory(this.tpl, Roo);
9158     }
9159     
9160     
9161     this.tpl.compile();
9162    
9163
9164      
9165     /** @private */
9166     this.addEvents({
9167         /**
9168          * @event beforeclick
9169          * Fires before a click is processed. Returns false to cancel the default action.
9170          * @param {Roo.View} this
9171          * @param {Number} index The index of the target node
9172          * @param {HTMLElement} node The target node
9173          * @param {Roo.EventObject} e The raw event object
9174          */
9175             "beforeclick" : true,
9176         /**
9177          * @event click
9178          * Fires when a template node is clicked.
9179          * @param {Roo.View} this
9180          * @param {Number} index The index of the target node
9181          * @param {HTMLElement} node The target node
9182          * @param {Roo.EventObject} e The raw event object
9183          */
9184             "click" : true,
9185         /**
9186          * @event dblclick
9187          * Fires when a template node is double clicked.
9188          * @param {Roo.View} this
9189          * @param {Number} index The index of the target node
9190          * @param {HTMLElement} node The target node
9191          * @param {Roo.EventObject} e The raw event object
9192          */
9193             "dblclick" : true,
9194         /**
9195          * @event contextmenu
9196          * Fires when a template node is right clicked.
9197          * @param {Roo.View} this
9198          * @param {Number} index The index of the target node
9199          * @param {HTMLElement} node The target node
9200          * @param {Roo.EventObject} e The raw event object
9201          */
9202             "contextmenu" : true,
9203         /**
9204          * @event selectionchange
9205          * Fires when the selected nodes change.
9206          * @param {Roo.View} this
9207          * @param {Array} selections Array of the selected nodes
9208          */
9209             "selectionchange" : true,
9210     
9211         /**
9212          * @event beforeselect
9213          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9214          * @param {Roo.View} this
9215          * @param {HTMLElement} node The node to be selected
9216          * @param {Array} selections Array of currently selected nodes
9217          */
9218             "beforeselect" : true,
9219         /**
9220          * @event preparedata
9221          * Fires on every row to render, to allow you to change the data.
9222          * @param {Roo.View} this
9223          * @param {Object} data to be rendered (change this)
9224          */
9225           "preparedata" : true
9226         });
9227
9228     this.el.on({
9229         "click": this.onClick,
9230         "dblclick": this.onDblClick,
9231         "contextmenu": this.onContextMenu,
9232         scope:this
9233     });
9234
9235     this.selections = [];
9236     this.nodes = [];
9237     this.cmp = new Roo.CompositeElementLite([]);
9238     if(this.store){
9239         this.store = Roo.factory(this.store, Roo.data);
9240         this.setStore(this.store, true);
9241     }
9242     Roo.View.superclass.constructor.call(this);
9243 };
9244
9245 Roo.extend(Roo.View, Roo.util.Observable, {
9246     
9247      /**
9248      * @cfg {Roo.data.Store} store Data store to load data from.
9249      */
9250     store : false,
9251     
9252     /**
9253      * @cfg {String|Roo.Element} el The container element.
9254      */
9255     el : '',
9256     
9257     /**
9258      * @cfg {String|Roo.Template} tpl The template used by this View 
9259      */
9260     tpl : false,
9261     
9262     /**
9263      * @cfg {String} selectedClass The css class to add to selected nodes
9264      */
9265     selectedClass : "x-view-selected",
9266      /**
9267      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9268      */
9269     emptyText : "",
9270     /**
9271      * @cfg {Boolean} multiSelect Allow multiple selection
9272      */
9273     multiSelect : false,
9274     /**
9275      * @cfg {Boolean} singleSelect Allow single selection
9276      */
9277     singleSelect:  false,
9278     
9279     /**
9280      * @cfg {Boolean} toggleSelect - selecting 
9281      */
9282     toggleSelect : false,
9283     
9284     /**
9285      * Returns the element this view is bound to.
9286      * @return {Roo.Element}
9287      */
9288     getEl : function(){
9289         return this.el;
9290     },
9291
9292     /**
9293      * Refreshes the view.
9294      */
9295     refresh : function(){
9296         var t = this.tpl;
9297         this.clearSelections();
9298         this.el.update("");
9299         var html = [];
9300         var records = this.store.getRange();
9301         if(records.length < 1){
9302             this.el.update(this.emptyText);
9303             return;
9304         }
9305         for(var i = 0, len = records.length; i < len; i++){
9306             var data = this.prepareData(records[i].data, i, records[i]);
9307             this.fireEvent("preparedata", this, data, i, records[i]);
9308             html[html.length] = t.apply(data);
9309         }
9310         this.el.update(html.join(""));
9311         this.nodes = this.el.dom.childNodes;
9312         this.updateIndexes(0);
9313     },
9314
9315     /**
9316      * Function to override to reformat the data that is sent to
9317      * the template for each node.
9318      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9319      * a JSON object for an UpdateManager bound view).
9320      */
9321     prepareData : function(data){
9322         return data;
9323     },
9324
9325     onUpdate : function(ds, record){
9326         this.clearSelections();
9327         var index = this.store.indexOf(record);
9328         var n = this.nodes[index];
9329         this.tpl.insertBefore(n, this.prepareData(record.data));
9330         n.parentNode.removeChild(n);
9331         this.updateIndexes(index, index);
9332     },
9333
9334     onAdd : function(ds, records, index){
9335         this.clearSelections();
9336         if(this.nodes.length == 0){
9337             this.refresh();
9338             return;
9339         }
9340         var n = this.nodes[index];
9341         for(var i = 0, len = records.length; i < len; i++){
9342             var d = this.prepareData(records[i].data);
9343             if(n){
9344                 this.tpl.insertBefore(n, d);
9345             }else{
9346                 this.tpl.append(this.el, d);
9347             }
9348         }
9349         this.updateIndexes(index);
9350     },
9351
9352     onRemove : function(ds, record, index){
9353         this.clearSelections();
9354         this.el.dom.removeChild(this.nodes[index]);
9355         this.updateIndexes(index);
9356     },
9357
9358     /**
9359      * Refresh an individual node.
9360      * @param {Number} index
9361      */
9362     refreshNode : function(index){
9363         this.onUpdate(this.store, this.store.getAt(index));
9364     },
9365
9366     updateIndexes : function(startIndex, endIndex){
9367         var ns = this.nodes;
9368         startIndex = startIndex || 0;
9369         endIndex = endIndex || ns.length - 1;
9370         for(var i = startIndex; i <= endIndex; i++){
9371             ns[i].nodeIndex = i;
9372         }
9373     },
9374
9375     /**
9376      * Changes the data store this view uses and refresh the view.
9377      * @param {Store} store
9378      */
9379     setStore : function(store, initial){
9380         if(!initial && this.store){
9381             this.store.un("datachanged", this.refresh);
9382             this.store.un("add", this.onAdd);
9383             this.store.un("remove", this.onRemove);
9384             this.store.un("update", this.onUpdate);
9385             this.store.un("clear", this.refresh);
9386         }
9387         if(store){
9388           
9389             store.on("datachanged", this.refresh, this);
9390             store.on("add", this.onAdd, this);
9391             store.on("remove", this.onRemove, this);
9392             store.on("update", this.onUpdate, this);
9393             store.on("clear", this.refresh, this);
9394         }
9395         
9396         if(store){
9397             this.refresh();
9398         }
9399     },
9400
9401     /**
9402      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9403      * @param {HTMLElement} node
9404      * @return {HTMLElement} The template node
9405      */
9406     findItemFromChild : function(node){
9407         var el = this.el.dom;
9408         if(!node || node.parentNode == el){
9409                     return node;
9410             }
9411             var p = node.parentNode;
9412             while(p && p != el){
9413             if(p.parentNode == el){
9414                 return p;
9415             }
9416             p = p.parentNode;
9417         }
9418             return null;
9419     },
9420
9421     /** @ignore */
9422     onClick : function(e){
9423         var item = this.findItemFromChild(e.getTarget());
9424         if(item){
9425             var index = this.indexOf(item);
9426             if(this.onItemClick(item, index, e) !== false){
9427                 this.fireEvent("click", this, index, item, e);
9428             }
9429         }else{
9430             this.clearSelections();
9431         }
9432     },
9433
9434     /** @ignore */
9435     onContextMenu : function(e){
9436         var item = this.findItemFromChild(e.getTarget());
9437         if(item){
9438             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9439         }
9440     },
9441
9442     /** @ignore */
9443     onDblClick : function(e){
9444         var item = this.findItemFromChild(e.getTarget());
9445         if(item){
9446             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9447         }
9448     },
9449
9450     onItemClick : function(item, index, e)
9451     {
9452         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9453             return false;
9454         }
9455         if (this.toggleSelect) {
9456             var m = this.isSelected(item) ? 'unselect' : 'select';
9457             Roo.log(m);
9458             var _t = this;
9459             _t[m](item, true, false);
9460             return true;
9461         }
9462         if(this.multiSelect || this.singleSelect){
9463             if(this.multiSelect && e.shiftKey && this.lastSelection){
9464                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9465             }else{
9466                 this.select(item, this.multiSelect && e.ctrlKey);
9467                 this.lastSelection = item;
9468             }
9469             e.preventDefault();
9470         }
9471         return true;
9472     },
9473
9474     /**
9475      * Get the number of selected nodes.
9476      * @return {Number}
9477      */
9478     getSelectionCount : function(){
9479         return this.selections.length;
9480     },
9481
9482     /**
9483      * Get the currently selected nodes.
9484      * @return {Array} An array of HTMLElements
9485      */
9486     getSelectedNodes : function(){
9487         return this.selections;
9488     },
9489
9490     /**
9491      * Get the indexes of the selected nodes.
9492      * @return {Array}
9493      */
9494     getSelectedIndexes : function(){
9495         var indexes = [], s = this.selections;
9496         for(var i = 0, len = s.length; i < len; i++){
9497             indexes.push(s[i].nodeIndex);
9498         }
9499         return indexes;
9500     },
9501
9502     /**
9503      * Clear all selections
9504      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9505      */
9506     clearSelections : function(suppressEvent){
9507         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9508             this.cmp.elements = this.selections;
9509             this.cmp.removeClass(this.selectedClass);
9510             this.selections = [];
9511             if(!suppressEvent){
9512                 this.fireEvent("selectionchange", this, this.selections);
9513             }
9514         }
9515     },
9516
9517     /**
9518      * Returns true if the passed node is selected
9519      * @param {HTMLElement/Number} node The node or node index
9520      * @return {Boolean}
9521      */
9522     isSelected : function(node){
9523         var s = this.selections;
9524         if(s.length < 1){
9525             return false;
9526         }
9527         node = this.getNode(node);
9528         return s.indexOf(node) !== -1;
9529     },
9530
9531     /**
9532      * Selects nodes.
9533      * @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
9534      * @param {Boolean} keepExisting (optional) true to keep existing selections
9535      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9536      */
9537     select : function(nodeInfo, keepExisting, suppressEvent){
9538         if(nodeInfo instanceof Array){
9539             if(!keepExisting){
9540                 this.clearSelections(true);
9541             }
9542             for(var i = 0, len = nodeInfo.length; i < len; i++){
9543                 this.select(nodeInfo[i], true, true);
9544             }
9545             return;
9546         } 
9547         var node = this.getNode(nodeInfo);
9548         if(!node || this.isSelected(node)){
9549             return; // already selected.
9550         }
9551         if(!keepExisting){
9552             this.clearSelections(true);
9553         }
9554         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9555             Roo.fly(node).addClass(this.selectedClass);
9556             this.selections.push(node);
9557             if(!suppressEvent){
9558                 this.fireEvent("selectionchange", this, this.selections);
9559             }
9560         }
9561         
9562         
9563     },
9564       /**
9565      * Unselects nodes.
9566      * @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
9567      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
9568      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9569      */
9570     unselect : function(nodeInfo, keepExisting, suppressEvent)
9571     {
9572         if(nodeInfo instanceof Array){
9573             Roo.each(this.selections, function(s) {
9574                 this.unselect(s, nodeInfo);
9575             }, this);
9576             return;
9577         }
9578         var node = this.getNode(nodeInfo);
9579         if(!node || !this.isSelected(node)){
9580             Roo.log("not selected");
9581             return; // not selected.
9582         }
9583         // fireevent???
9584         var ns = [];
9585         Roo.each(this.selections, function(s) {
9586             if (s == node ) {
9587                 Roo.fly(node).removeClass(this.selectedClass);
9588
9589                 return;
9590             }
9591             ns.push(s);
9592         },this);
9593         
9594         this.selections= ns;
9595         this.fireEvent("selectionchange", this, this.selections);
9596     },
9597
9598     /**
9599      * Gets a template node.
9600      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9601      * @return {HTMLElement} The node or null if it wasn't found
9602      */
9603     getNode : function(nodeInfo){
9604         if(typeof nodeInfo == "string"){
9605             return document.getElementById(nodeInfo);
9606         }else if(typeof nodeInfo == "number"){
9607             return this.nodes[nodeInfo];
9608         }
9609         return nodeInfo;
9610     },
9611
9612     /**
9613      * Gets a range template nodes.
9614      * @param {Number} startIndex
9615      * @param {Number} endIndex
9616      * @return {Array} An array of nodes
9617      */
9618     getNodes : function(start, end){
9619         var ns = this.nodes;
9620         start = start || 0;
9621         end = typeof end == "undefined" ? ns.length - 1 : end;
9622         var nodes = [];
9623         if(start <= end){
9624             for(var i = start; i <= end; i++){
9625                 nodes.push(ns[i]);
9626             }
9627         } else{
9628             for(var i = start; i >= end; i--){
9629                 nodes.push(ns[i]);
9630             }
9631         }
9632         return nodes;
9633     },
9634
9635     /**
9636      * Finds the index of the passed node
9637      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9638      * @return {Number} The index of the node or -1
9639      */
9640     indexOf : function(node){
9641         node = this.getNode(node);
9642         if(typeof node.nodeIndex == "number"){
9643             return node.nodeIndex;
9644         }
9645         var ns = this.nodes;
9646         for(var i = 0, len = ns.length; i < len; i++){
9647             if(ns[i] == node){
9648                 return i;
9649             }
9650         }
9651         return -1;
9652     }
9653 });
9654 /*
9655  * Based on:
9656  * Ext JS Library 1.1.1
9657  * Copyright(c) 2006-2007, Ext JS, LLC.
9658  *
9659  * Originally Released Under LGPL - original licence link has changed is not relivant.
9660  *
9661  * Fork - LGPL
9662  * <script type="text/javascript">
9663  */
9664
9665 /**
9666  * @class Roo.JsonView
9667  * @extends Roo.View
9668  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9669 <pre><code>
9670 var view = new Roo.JsonView({
9671     container: "my-element",
9672     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
9673     multiSelect: true, 
9674     jsonRoot: "data" 
9675 });
9676
9677 // listen for node click?
9678 view.on("click", function(vw, index, node, e){
9679     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9680 });
9681
9682 // direct load of JSON data
9683 view.load("foobar.php");
9684
9685 // Example from my blog list
9686 var tpl = new Roo.Template(
9687     '&lt;div class="entry"&gt;' +
9688     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
9689     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
9690     "&lt;/div&gt;&lt;hr /&gt;"
9691 );
9692
9693 var moreView = new Roo.JsonView({
9694     container :  "entry-list", 
9695     template : tpl,
9696     jsonRoot: "posts"
9697 });
9698 moreView.on("beforerender", this.sortEntries, this);
9699 moreView.load({
9700     url: "/blog/get-posts.php",
9701     params: "allposts=true",
9702     text: "Loading Blog Entries..."
9703 });
9704 </code></pre>
9705
9706 * Note: old code is supported with arguments : (container, template, config)
9707
9708
9709  * @constructor
9710  * Create a new JsonView
9711  * 
9712  * @param {Object} config The config object
9713  * 
9714  */
9715 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9716     
9717     
9718     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9719
9720     var um = this.el.getUpdateManager();
9721     um.setRenderer(this);
9722     um.on("update", this.onLoad, this);
9723     um.on("failure", this.onLoadException, this);
9724
9725     /**
9726      * @event beforerender
9727      * Fires before rendering of the downloaded JSON data.
9728      * @param {Roo.JsonView} this
9729      * @param {Object} data The JSON data loaded
9730      */
9731     /**
9732      * @event load
9733      * Fires when data is loaded.
9734      * @param {Roo.JsonView} this
9735      * @param {Object} data The JSON data loaded
9736      * @param {Object} response The raw Connect response object
9737      */
9738     /**
9739      * @event loadexception
9740      * Fires when loading fails.
9741      * @param {Roo.JsonView} this
9742      * @param {Object} response The raw Connect response object
9743      */
9744     this.addEvents({
9745         'beforerender' : true,
9746         'load' : true,
9747         'loadexception' : true
9748     });
9749 };
9750 Roo.extend(Roo.JsonView, Roo.View, {
9751     /**
9752      * @type {String} The root property in the loaded JSON object that contains the data
9753      */
9754     jsonRoot : "",
9755
9756     /**
9757      * Refreshes the view.
9758      */
9759     refresh : function(){
9760         this.clearSelections();
9761         this.el.update("");
9762         var html = [];
9763         var o = this.jsonData;
9764         if(o && o.length > 0){
9765             for(var i = 0, len = o.length; i < len; i++){
9766                 var data = this.prepareData(o[i], i, o);
9767                 html[html.length] = this.tpl.apply(data);
9768             }
9769         }else{
9770             html.push(this.emptyText);
9771         }
9772         this.el.update(html.join(""));
9773         this.nodes = this.el.dom.childNodes;
9774         this.updateIndexes(0);
9775     },
9776
9777     /**
9778      * 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.
9779      * @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:
9780      <pre><code>
9781      view.load({
9782          url: "your-url.php",
9783          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9784          callback: yourFunction,
9785          scope: yourObject, //(optional scope)
9786          discardUrl: false,
9787          nocache: false,
9788          text: "Loading...",
9789          timeout: 30,
9790          scripts: false
9791      });
9792      </code></pre>
9793      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9794      * 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.
9795      * @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}
9796      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9797      * @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.
9798      */
9799     load : function(){
9800         var um = this.el.getUpdateManager();
9801         um.update.apply(um, arguments);
9802     },
9803
9804     render : function(el, response){
9805         this.clearSelections();
9806         this.el.update("");
9807         var o;
9808         try{
9809             o = Roo.util.JSON.decode(response.responseText);
9810             if(this.jsonRoot){
9811                 
9812                 o = o[this.jsonRoot];
9813             }
9814         } catch(e){
9815         }
9816         /**
9817          * The current JSON data or null
9818          */
9819         this.jsonData = o;
9820         this.beforeRender();
9821         this.refresh();
9822     },
9823
9824 /**
9825  * Get the number of records in the current JSON dataset
9826  * @return {Number}
9827  */
9828     getCount : function(){
9829         return this.jsonData ? this.jsonData.length : 0;
9830     },
9831
9832 /**
9833  * Returns the JSON object for the specified node(s)
9834  * @param {HTMLElement/Array} node The node or an array of nodes
9835  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9836  * you get the JSON object for the node
9837  */
9838     getNodeData : function(node){
9839         if(node instanceof Array){
9840             var data = [];
9841             for(var i = 0, len = node.length; i < len; i++){
9842                 data.push(this.getNodeData(node[i]));
9843             }
9844             return data;
9845         }
9846         return this.jsonData[this.indexOf(node)] || null;
9847     },
9848
9849     beforeRender : function(){
9850         this.snapshot = this.jsonData;
9851         if(this.sortInfo){
9852             this.sort.apply(this, this.sortInfo);
9853         }
9854         this.fireEvent("beforerender", this, this.jsonData);
9855     },
9856
9857     onLoad : function(el, o){
9858         this.fireEvent("load", this, this.jsonData, o);
9859     },
9860
9861     onLoadException : function(el, o){
9862         this.fireEvent("loadexception", this, o);
9863     },
9864
9865 /**
9866  * Filter the data by a specific property.
9867  * @param {String} property A property on your JSON objects
9868  * @param {String/RegExp} value Either string that the property values
9869  * should start with, or a RegExp to test against the property
9870  */
9871     filter : function(property, value){
9872         if(this.jsonData){
9873             var data = [];
9874             var ss = this.snapshot;
9875             if(typeof value == "string"){
9876                 var vlen = value.length;
9877                 if(vlen == 0){
9878                     this.clearFilter();
9879                     return;
9880                 }
9881                 value = value.toLowerCase();
9882                 for(var i = 0, len = ss.length; i < len; i++){
9883                     var o = ss[i];
9884                     if(o[property].substr(0, vlen).toLowerCase() == value){
9885                         data.push(o);
9886                     }
9887                 }
9888             } else if(value.exec){ // regex?
9889                 for(var i = 0, len = ss.length; i < len; i++){
9890                     var o = ss[i];
9891                     if(value.test(o[property])){
9892                         data.push(o);
9893                     }
9894                 }
9895             } else{
9896                 return;
9897             }
9898             this.jsonData = data;
9899             this.refresh();
9900         }
9901     },
9902
9903 /**
9904  * Filter by a function. The passed function will be called with each
9905  * object in the current dataset. If the function returns true the value is kept,
9906  * otherwise it is filtered.
9907  * @param {Function} fn
9908  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
9909  */
9910     filterBy : function(fn, scope){
9911         if(this.jsonData){
9912             var data = [];
9913             var ss = this.snapshot;
9914             for(var i = 0, len = ss.length; i < len; i++){
9915                 var o = ss[i];
9916                 if(fn.call(scope || this, o)){
9917                     data.push(o);
9918                 }
9919             }
9920             this.jsonData = data;
9921             this.refresh();
9922         }
9923     },
9924
9925 /**
9926  * Clears the current filter.
9927  */
9928     clearFilter : function(){
9929         if(this.snapshot && this.jsonData != this.snapshot){
9930             this.jsonData = this.snapshot;
9931             this.refresh();
9932         }
9933     },
9934
9935
9936 /**
9937  * Sorts the data for this view and refreshes it.
9938  * @param {String} property A property on your JSON objects to sort on
9939  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
9940  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
9941  */
9942     sort : function(property, dir, sortType){
9943         this.sortInfo = Array.prototype.slice.call(arguments, 0);
9944         if(this.jsonData){
9945             var p = property;
9946             var dsc = dir && dir.toLowerCase() == "desc";
9947             var f = function(o1, o2){
9948                 var v1 = sortType ? sortType(o1[p]) : o1[p];
9949                 var v2 = sortType ? sortType(o2[p]) : o2[p];
9950                 ;
9951                 if(v1 < v2){
9952                     return dsc ? +1 : -1;
9953                 } else if(v1 > v2){
9954                     return dsc ? -1 : +1;
9955                 } else{
9956                     return 0;
9957                 }
9958             };
9959             this.jsonData.sort(f);
9960             this.refresh();
9961             if(this.jsonData != this.snapshot){
9962                 this.snapshot.sort(f);
9963             }
9964         }
9965     }
9966 });/*
9967  * Based on:
9968  * Ext JS Library 1.1.1
9969  * Copyright(c) 2006-2007, Ext JS, LLC.
9970  *
9971  * Originally Released Under LGPL - original licence link has changed is not relivant.
9972  *
9973  * Fork - LGPL
9974  * <script type="text/javascript">
9975  */
9976  
9977
9978 /**
9979  * @class Roo.ColorPalette
9980  * @extends Roo.Component
9981  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
9982  * Here's an example of typical usage:
9983  * <pre><code>
9984 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
9985 cp.render('my-div');
9986
9987 cp.on('select', function(palette, selColor){
9988     // do something with selColor
9989 });
9990 </code></pre>
9991  * @constructor
9992  * Create a new ColorPalette
9993  * @param {Object} config The config object
9994  */
9995 Roo.ColorPalette = function(config){
9996     Roo.ColorPalette.superclass.constructor.call(this, config);
9997     this.addEvents({
9998         /**
9999              * @event select
10000              * Fires when a color is selected
10001              * @param {ColorPalette} this
10002              * @param {String} color The 6-digit color hex code (without the # symbol)
10003              */
10004         select: true
10005     });
10006
10007     if(this.handler){
10008         this.on("select", this.handler, this.scope, true);
10009     }
10010 };
10011 Roo.extend(Roo.ColorPalette, Roo.Component, {
10012     /**
10013      * @cfg {String} itemCls
10014      * The CSS class to apply to the containing element (defaults to "x-color-palette")
10015      */
10016     itemCls : "x-color-palette",
10017     /**
10018      * @cfg {String} value
10019      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
10020      * the hex codes are case-sensitive.
10021      */
10022     value : null,
10023     clickEvent:'click',
10024     // private
10025     ctype: "Roo.ColorPalette",
10026
10027     /**
10028      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
10029      */
10030     allowReselect : false,
10031
10032     /**
10033      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
10034      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
10035      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
10036      * of colors with the width setting until the box is symmetrical.</p>
10037      * <p>You can override individual colors if needed:</p>
10038      * <pre><code>
10039 var cp = new Roo.ColorPalette();
10040 cp.colors[0] = "FF0000";  // change the first box to red
10041 </code></pre>
10042
10043 Or you can provide a custom array of your own for complete control:
10044 <pre><code>
10045 var cp = new Roo.ColorPalette();
10046 cp.colors = ["000000", "993300", "333300"];
10047 </code></pre>
10048      * @type Array
10049      */
10050     colors : [
10051         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
10052         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
10053         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
10054         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
10055         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
10056     ],
10057
10058     // private
10059     onRender : function(container, position){
10060         var t = new Roo.MasterTemplate(
10061             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
10062         );
10063         var c = this.colors;
10064         for(var i = 0, len = c.length; i < len; i++){
10065             t.add([c[i]]);
10066         }
10067         var el = document.createElement("div");
10068         el.className = this.itemCls;
10069         t.overwrite(el);
10070         container.dom.insertBefore(el, position);
10071         this.el = Roo.get(el);
10072         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
10073         if(this.clickEvent != 'click'){
10074             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
10075         }
10076     },
10077
10078     // private
10079     afterRender : function(){
10080         Roo.ColorPalette.superclass.afterRender.call(this);
10081         if(this.value){
10082             var s = this.value;
10083             this.value = null;
10084             this.select(s);
10085         }
10086     },
10087
10088     // private
10089     handleClick : function(e, t){
10090         e.preventDefault();
10091         if(!this.disabled){
10092             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
10093             this.select(c.toUpperCase());
10094         }
10095     },
10096
10097     /**
10098      * Selects the specified color in the palette (fires the select event)
10099      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
10100      */
10101     select : function(color){
10102         color = color.replace("#", "");
10103         if(color != this.value || this.allowReselect){
10104             var el = this.el;
10105             if(this.value){
10106                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
10107             }
10108             el.child("a.color-"+color).addClass("x-color-palette-sel");
10109             this.value = color;
10110             this.fireEvent("select", this, color);
10111         }
10112     }
10113 });/*
10114  * Based on:
10115  * Ext JS Library 1.1.1
10116  * Copyright(c) 2006-2007, Ext JS, LLC.
10117  *
10118  * Originally Released Under LGPL - original licence link has changed is not relivant.
10119  *
10120  * Fork - LGPL
10121  * <script type="text/javascript">
10122  */
10123  
10124 /**
10125  * @class Roo.DatePicker
10126  * @extends Roo.Component
10127  * Simple date picker class.
10128  * @constructor
10129  * Create a new DatePicker
10130  * @param {Object} config The config object
10131  */
10132 Roo.DatePicker = function(config){
10133     Roo.DatePicker.superclass.constructor.call(this, config);
10134
10135     this.value = config && config.value ?
10136                  config.value.clearTime() : new Date().clearTime();
10137
10138     this.addEvents({
10139         /**
10140              * @event select
10141              * Fires when a date is selected
10142              * @param {DatePicker} this
10143              * @param {Date} date The selected date
10144              */
10145         'select': true,
10146         /**
10147              * @event monthchange
10148              * Fires when the displayed month changes 
10149              * @param {DatePicker} this
10150              * @param {Date} date The selected month
10151              */
10152         'monthchange': true
10153     });
10154
10155     if(this.handler){
10156         this.on("select", this.handler,  this.scope || this);
10157     }
10158     // build the disabledDatesRE
10159     if(!this.disabledDatesRE && this.disabledDates){
10160         var dd = this.disabledDates;
10161         var re = "(?:";
10162         for(var i = 0; i < dd.length; i++){
10163             re += dd[i];
10164             if(i != dd.length-1) re += "|";
10165         }
10166         this.disabledDatesRE = new RegExp(re + ")");
10167     }
10168 };
10169
10170 Roo.extend(Roo.DatePicker, Roo.Component, {
10171     /**
10172      * @cfg {String} todayText
10173      * The text to display on the button that selects the current date (defaults to "Today")
10174      */
10175     todayText : "Today",
10176     /**
10177      * @cfg {String} okText
10178      * The text to display on the ok button
10179      */
10180     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
10181     /**
10182      * @cfg {String} cancelText
10183      * The text to display on the cancel button
10184      */
10185     cancelText : "Cancel",
10186     /**
10187      * @cfg {String} todayTip
10188      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
10189      */
10190     todayTip : "{0} (Spacebar)",
10191     /**
10192      * @cfg {Date} minDate
10193      * Minimum allowable date (JavaScript date object, defaults to null)
10194      */
10195     minDate : null,
10196     /**
10197      * @cfg {Date} maxDate
10198      * Maximum allowable date (JavaScript date object, defaults to null)
10199      */
10200     maxDate : null,
10201     /**
10202      * @cfg {String} minText
10203      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
10204      */
10205     minText : "This date is before the minimum date",
10206     /**
10207      * @cfg {String} maxText
10208      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
10209      */
10210     maxText : "This date is after the maximum date",
10211     /**
10212      * @cfg {String} format
10213      * The default date format string which can be overriden for localization support.  The format must be
10214      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
10215      */
10216     format : "m/d/y",
10217     /**
10218      * @cfg {Array} disabledDays
10219      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
10220      */
10221     disabledDays : null,
10222     /**
10223      * @cfg {String} disabledDaysText
10224      * The tooltip to display when the date falls on a disabled day (defaults to "")
10225      */
10226     disabledDaysText : "",
10227     /**
10228      * @cfg {RegExp} disabledDatesRE
10229      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
10230      */
10231     disabledDatesRE : null,
10232     /**
10233      * @cfg {String} disabledDatesText
10234      * The tooltip text to display when the date falls on a disabled date (defaults to "")
10235      */
10236     disabledDatesText : "",
10237     /**
10238      * @cfg {Boolean} constrainToViewport
10239      * True to constrain the date picker to the viewport (defaults to true)
10240      */
10241     constrainToViewport : true,
10242     /**
10243      * @cfg {Array} monthNames
10244      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
10245      */
10246     monthNames : Date.monthNames,
10247     /**
10248      * @cfg {Array} dayNames
10249      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
10250      */
10251     dayNames : Date.dayNames,
10252     /**
10253      * @cfg {String} nextText
10254      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
10255      */
10256     nextText: 'Next Month (Control+Right)',
10257     /**
10258      * @cfg {String} prevText
10259      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
10260      */
10261     prevText: 'Previous Month (Control+Left)',
10262     /**
10263      * @cfg {String} monthYearText
10264      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
10265      */
10266     monthYearText: 'Choose a month (Control+Up/Down to move years)',
10267     /**
10268      * @cfg {Number} startDay
10269      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
10270      */
10271     startDay : 0,
10272     /**
10273      * @cfg {Bool} showClear
10274      * Show a clear button (usefull for date form elements that can be blank.)
10275      */
10276     
10277     showClear: false,
10278     
10279     /**
10280      * Sets the value of the date field
10281      * @param {Date} value The date to set
10282      */
10283     setValue : function(value){
10284         var old = this.value;
10285         this.value = value.clearTime(true);
10286         if(this.el){
10287             this.update(this.value);
10288         }
10289     },
10290
10291     /**
10292      * Gets the current selected value of the date field
10293      * @return {Date} The selected date
10294      */
10295     getValue : function(){
10296         return this.value;
10297     },
10298
10299     // private
10300     focus : function(){
10301         if(this.el){
10302             this.update(this.activeDate);
10303         }
10304     },
10305
10306     // private
10307     onRender : function(container, position){
10308         var m = [
10309              '<table cellspacing="0">',
10310                 '<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>',
10311                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
10312         var dn = this.dayNames;
10313         for(var i = 0; i < 7; i++){
10314             var d = this.startDay+i;
10315             if(d > 6){
10316                 d = d-7;
10317             }
10318             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
10319         }
10320         m[m.length] = "</tr></thead><tbody><tr>";
10321         for(var i = 0; i < 42; i++) {
10322             if(i % 7 == 0 && i != 0){
10323                 m[m.length] = "</tr><tr>";
10324             }
10325             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
10326         }
10327         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
10328             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
10329
10330         var el = document.createElement("div");
10331         el.className = "x-date-picker";
10332         el.innerHTML = m.join("");
10333
10334         container.dom.insertBefore(el, position);
10335
10336         this.el = Roo.get(el);
10337         this.eventEl = Roo.get(el.firstChild);
10338
10339         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
10340             handler: this.showPrevMonth,
10341             scope: this,
10342             preventDefault:true,
10343             stopDefault:true
10344         });
10345
10346         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
10347             handler: this.showNextMonth,
10348             scope: this,
10349             preventDefault:true,
10350             stopDefault:true
10351         });
10352
10353         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
10354
10355         this.monthPicker = this.el.down('div.x-date-mp');
10356         this.monthPicker.enableDisplayMode('block');
10357         
10358         var kn = new Roo.KeyNav(this.eventEl, {
10359             "left" : function(e){
10360                 e.ctrlKey ?
10361                     this.showPrevMonth() :
10362                     this.update(this.activeDate.add("d", -1));
10363             },
10364
10365             "right" : function(e){
10366                 e.ctrlKey ?
10367                     this.showNextMonth() :
10368                     this.update(this.activeDate.add("d", 1));
10369             },
10370
10371             "up" : function(e){
10372                 e.ctrlKey ?
10373                     this.showNextYear() :
10374                     this.update(this.activeDate.add("d", -7));
10375             },
10376
10377             "down" : function(e){
10378                 e.ctrlKey ?
10379                     this.showPrevYear() :
10380                     this.update(this.activeDate.add("d", 7));
10381             },
10382
10383             "pageUp" : function(e){
10384                 this.showNextMonth();
10385             },
10386
10387             "pageDown" : function(e){
10388                 this.showPrevMonth();
10389             },
10390
10391             "enter" : function(e){
10392                 e.stopPropagation();
10393                 return true;
10394             },
10395
10396             scope : this
10397         });
10398
10399         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
10400
10401         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
10402
10403         this.el.unselectable();
10404         
10405         this.cells = this.el.select("table.x-date-inner tbody td");
10406         this.textNodes = this.el.query("table.x-date-inner tbody span");
10407
10408         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
10409             text: "&#160;",
10410             tooltip: this.monthYearText
10411         });
10412
10413         this.mbtn.on('click', this.showMonthPicker, this);
10414         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
10415
10416
10417         var today = (new Date()).dateFormat(this.format);
10418         
10419         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
10420         if (this.showClear) {
10421             baseTb.add( new Roo.Toolbar.Fill());
10422         }
10423         baseTb.add({
10424             text: String.format(this.todayText, today),
10425             tooltip: String.format(this.todayTip, today),
10426             handler: this.selectToday,
10427             scope: this
10428         });
10429         
10430         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
10431             
10432         //});
10433         if (this.showClear) {
10434             
10435             baseTb.add( new Roo.Toolbar.Fill());
10436             baseTb.add({
10437                 text: '&#160;',
10438                 cls: 'x-btn-icon x-btn-clear',
10439                 handler: function() {
10440                     //this.value = '';
10441                     this.fireEvent("select", this, '');
10442                 },
10443                 scope: this
10444             });
10445         }
10446         
10447         
10448         if(Roo.isIE){
10449             this.el.repaint();
10450         }
10451         this.update(this.value);
10452     },
10453
10454     createMonthPicker : function(){
10455         if(!this.monthPicker.dom.firstChild){
10456             var buf = ['<table border="0" cellspacing="0">'];
10457             for(var i = 0; i < 6; i++){
10458                 buf.push(
10459                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
10460                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
10461                     i == 0 ?
10462                     '<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>' :
10463                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
10464                 );
10465             }
10466             buf.push(
10467                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
10468                     this.okText,
10469                     '</button><button type="button" class="x-date-mp-cancel">',
10470                     this.cancelText,
10471                     '</button></td></tr>',
10472                 '</table>'
10473             );
10474             this.monthPicker.update(buf.join(''));
10475             this.monthPicker.on('click', this.onMonthClick, this);
10476             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
10477
10478             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
10479             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
10480
10481             this.mpMonths.each(function(m, a, i){
10482                 i += 1;
10483                 if((i%2) == 0){
10484                     m.dom.xmonth = 5 + Math.round(i * .5);
10485                 }else{
10486                     m.dom.xmonth = Math.round((i-1) * .5);
10487                 }
10488             });
10489         }
10490     },
10491
10492     showMonthPicker : function(){
10493         this.createMonthPicker();
10494         var size = this.el.getSize();
10495         this.monthPicker.setSize(size);
10496         this.monthPicker.child('table').setSize(size);
10497
10498         this.mpSelMonth = (this.activeDate || this.value).getMonth();
10499         this.updateMPMonth(this.mpSelMonth);
10500         this.mpSelYear = (this.activeDate || this.value).getFullYear();
10501         this.updateMPYear(this.mpSelYear);
10502
10503         this.monthPicker.slideIn('t', {duration:.2});
10504     },
10505
10506     updateMPYear : function(y){
10507         this.mpyear = y;
10508         var ys = this.mpYears.elements;
10509         for(var i = 1; i <= 10; i++){
10510             var td = ys[i-1], y2;
10511             if((i%2) == 0){
10512                 y2 = y + Math.round(i * .5);
10513                 td.firstChild.innerHTML = y2;
10514                 td.xyear = y2;
10515             }else{
10516                 y2 = y - (5-Math.round(i * .5));
10517                 td.firstChild.innerHTML = y2;
10518                 td.xyear = y2;
10519             }
10520             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
10521         }
10522     },
10523
10524     updateMPMonth : function(sm){
10525         this.mpMonths.each(function(m, a, i){
10526             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
10527         });
10528     },
10529
10530     selectMPMonth: function(m){
10531         
10532     },
10533
10534     onMonthClick : function(e, t){
10535         e.stopEvent();
10536         var el = new Roo.Element(t), pn;
10537         if(el.is('button.x-date-mp-cancel')){
10538             this.hideMonthPicker();
10539         }
10540         else if(el.is('button.x-date-mp-ok')){
10541             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10542             this.hideMonthPicker();
10543         }
10544         else if(pn = el.up('td.x-date-mp-month', 2)){
10545             this.mpMonths.removeClass('x-date-mp-sel');
10546             pn.addClass('x-date-mp-sel');
10547             this.mpSelMonth = pn.dom.xmonth;
10548         }
10549         else if(pn = el.up('td.x-date-mp-year', 2)){
10550             this.mpYears.removeClass('x-date-mp-sel');
10551             pn.addClass('x-date-mp-sel');
10552             this.mpSelYear = pn.dom.xyear;
10553         }
10554         else if(el.is('a.x-date-mp-prev')){
10555             this.updateMPYear(this.mpyear-10);
10556         }
10557         else if(el.is('a.x-date-mp-next')){
10558             this.updateMPYear(this.mpyear+10);
10559         }
10560     },
10561
10562     onMonthDblClick : function(e, t){
10563         e.stopEvent();
10564         var el = new Roo.Element(t), pn;
10565         if(pn = el.up('td.x-date-mp-month', 2)){
10566             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
10567             this.hideMonthPicker();
10568         }
10569         else if(pn = el.up('td.x-date-mp-year', 2)){
10570             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10571             this.hideMonthPicker();
10572         }
10573     },
10574
10575     hideMonthPicker : function(disableAnim){
10576         if(this.monthPicker){
10577             if(disableAnim === true){
10578                 this.monthPicker.hide();
10579             }else{
10580                 this.monthPicker.slideOut('t', {duration:.2});
10581             }
10582         }
10583     },
10584
10585     // private
10586     showPrevMonth : function(e){
10587         this.update(this.activeDate.add("mo", -1));
10588     },
10589
10590     // private
10591     showNextMonth : function(e){
10592         this.update(this.activeDate.add("mo", 1));
10593     },
10594
10595     // private
10596     showPrevYear : function(){
10597         this.update(this.activeDate.add("y", -1));
10598     },
10599
10600     // private
10601     showNextYear : function(){
10602         this.update(this.activeDate.add("y", 1));
10603     },
10604
10605     // private
10606     handleMouseWheel : function(e){
10607         var delta = e.getWheelDelta();
10608         if(delta > 0){
10609             this.showPrevMonth();
10610             e.stopEvent();
10611         } else if(delta < 0){
10612             this.showNextMonth();
10613             e.stopEvent();
10614         }
10615     },
10616
10617     // private
10618     handleDateClick : function(e, t){
10619         e.stopEvent();
10620         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10621             this.setValue(new Date(t.dateValue));
10622             this.fireEvent("select", this, this.value);
10623         }
10624     },
10625
10626     // private
10627     selectToday : function(){
10628         this.setValue(new Date().clearTime());
10629         this.fireEvent("select", this, this.value);
10630     },
10631
10632     // private
10633     update : function(date)
10634     {
10635         var vd = this.activeDate;
10636         this.activeDate = date;
10637         if(vd && this.el){
10638             var t = date.getTime();
10639             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10640                 this.cells.removeClass("x-date-selected");
10641                 this.cells.each(function(c){
10642                    if(c.dom.firstChild.dateValue == t){
10643                        c.addClass("x-date-selected");
10644                        setTimeout(function(){
10645                             try{c.dom.firstChild.focus();}catch(e){}
10646                        }, 50);
10647                        return false;
10648                    }
10649                 });
10650                 return;
10651             }
10652         }
10653         
10654         var days = date.getDaysInMonth();
10655         var firstOfMonth = date.getFirstDateOfMonth();
10656         var startingPos = firstOfMonth.getDay()-this.startDay;
10657
10658         if(startingPos <= this.startDay){
10659             startingPos += 7;
10660         }
10661
10662         var pm = date.add("mo", -1);
10663         var prevStart = pm.getDaysInMonth()-startingPos;
10664
10665         var cells = this.cells.elements;
10666         var textEls = this.textNodes;
10667         days += startingPos;
10668
10669         // convert everything to numbers so it's fast
10670         var day = 86400000;
10671         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10672         var today = new Date().clearTime().getTime();
10673         var sel = date.clearTime().getTime();
10674         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10675         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10676         var ddMatch = this.disabledDatesRE;
10677         var ddText = this.disabledDatesText;
10678         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10679         var ddaysText = this.disabledDaysText;
10680         var format = this.format;
10681
10682         var setCellClass = function(cal, cell){
10683             cell.title = "";
10684             var t = d.getTime();
10685             cell.firstChild.dateValue = t;
10686             if(t == today){
10687                 cell.className += " x-date-today";
10688                 cell.title = cal.todayText;
10689             }
10690             if(t == sel){
10691                 cell.className += " x-date-selected";
10692                 setTimeout(function(){
10693                     try{cell.firstChild.focus();}catch(e){}
10694                 }, 50);
10695             }
10696             // disabling
10697             if(t < min) {
10698                 cell.className = " x-date-disabled";
10699                 cell.title = cal.minText;
10700                 return;
10701             }
10702             if(t > max) {
10703                 cell.className = " x-date-disabled";
10704                 cell.title = cal.maxText;
10705                 return;
10706             }
10707             if(ddays){
10708                 if(ddays.indexOf(d.getDay()) != -1){
10709                     cell.title = ddaysText;
10710                     cell.className = " x-date-disabled";
10711                 }
10712             }
10713             if(ddMatch && format){
10714                 var fvalue = d.dateFormat(format);
10715                 if(ddMatch.test(fvalue)){
10716                     cell.title = ddText.replace("%0", fvalue);
10717                     cell.className = " x-date-disabled";
10718                 }
10719             }
10720         };
10721
10722         var i = 0;
10723         for(; i < startingPos; i++) {
10724             textEls[i].innerHTML = (++prevStart);
10725             d.setDate(d.getDate()+1);
10726             cells[i].className = "x-date-prevday";
10727             setCellClass(this, cells[i]);
10728         }
10729         for(; i < days; i++){
10730             intDay = i - startingPos + 1;
10731             textEls[i].innerHTML = (intDay);
10732             d.setDate(d.getDate()+1);
10733             cells[i].className = "x-date-active";
10734             setCellClass(this, cells[i]);
10735         }
10736         var extraDays = 0;
10737         for(; i < 42; i++) {
10738              textEls[i].innerHTML = (++extraDays);
10739              d.setDate(d.getDate()+1);
10740              cells[i].className = "x-date-nextday";
10741              setCellClass(this, cells[i]);
10742         }
10743
10744         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10745         this.fireEvent('monthchange', this, date);
10746         
10747         if(!this.internalRender){
10748             var main = this.el.dom.firstChild;
10749             var w = main.offsetWidth;
10750             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10751             Roo.fly(main).setWidth(w);
10752             this.internalRender = true;
10753             // opera does not respect the auto grow header center column
10754             // then, after it gets a width opera refuses to recalculate
10755             // without a second pass
10756             if(Roo.isOpera && !this.secondPass){
10757                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10758                 this.secondPass = true;
10759                 this.update.defer(10, this, [date]);
10760             }
10761         }
10762         
10763         
10764     }
10765 });        /*
10766  * Based on:
10767  * Ext JS Library 1.1.1
10768  * Copyright(c) 2006-2007, Ext JS, LLC.
10769  *
10770  * Originally Released Under LGPL - original licence link has changed is not relivant.
10771  *
10772  * Fork - LGPL
10773  * <script type="text/javascript">
10774  */
10775 /**
10776  * @class Roo.TabPanel
10777  * @extends Roo.util.Observable
10778  * A lightweight tab container.
10779  * <br><br>
10780  * Usage:
10781  * <pre><code>
10782 // basic tabs 1, built from existing content
10783 var tabs = new Roo.TabPanel("tabs1");
10784 tabs.addTab("script", "View Script");
10785 tabs.addTab("markup", "View Markup");
10786 tabs.activate("script");
10787
10788 // more advanced tabs, built from javascript
10789 var jtabs = new Roo.TabPanel("jtabs");
10790 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10791
10792 // set up the UpdateManager
10793 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10794 var updater = tab2.getUpdateManager();
10795 updater.setDefaultUrl("ajax1.htm");
10796 tab2.on('activate', updater.refresh, updater, true);
10797
10798 // Use setUrl for Ajax loading
10799 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10800 tab3.setUrl("ajax2.htm", null, true);
10801
10802 // Disabled tab
10803 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10804 tab4.disable();
10805
10806 jtabs.activate("jtabs-1");
10807  * </code></pre>
10808  * @constructor
10809  * Create a new TabPanel.
10810  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10811  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10812  */
10813 Roo.TabPanel = function(container, config){
10814     /**
10815     * The container element for this TabPanel.
10816     * @type Roo.Element
10817     */
10818     this.el = Roo.get(container, true);
10819     if(config){
10820         if(typeof config == "boolean"){
10821             this.tabPosition = config ? "bottom" : "top";
10822         }else{
10823             Roo.apply(this, config);
10824         }
10825     }
10826     if(this.tabPosition == "bottom"){
10827         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10828         this.el.addClass("x-tabs-bottom");
10829     }
10830     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10831     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10832     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10833     if(Roo.isIE){
10834         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10835     }
10836     if(this.tabPosition != "bottom"){
10837         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
10838          * @type Roo.Element
10839          */
10840         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10841         this.el.addClass("x-tabs-top");
10842     }
10843     this.items = [];
10844
10845     this.bodyEl.setStyle("position", "relative");
10846
10847     this.active = null;
10848     this.activateDelegate = this.activate.createDelegate(this);
10849
10850     this.addEvents({
10851         /**
10852          * @event tabchange
10853          * Fires when the active tab changes
10854          * @param {Roo.TabPanel} this
10855          * @param {Roo.TabPanelItem} activePanel The new active tab
10856          */
10857         "tabchange": true,
10858         /**
10859          * @event beforetabchange
10860          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
10861          * @param {Roo.TabPanel} this
10862          * @param {Object} e Set cancel to true on this object to cancel the tab change
10863          * @param {Roo.TabPanelItem} tab The tab being changed to
10864          */
10865         "beforetabchange" : true
10866     });
10867
10868     Roo.EventManager.onWindowResize(this.onResize, this);
10869     this.cpad = this.el.getPadding("lr");
10870     this.hiddenCount = 0;
10871
10872
10873     // toolbar on the tabbar support...
10874     if (this.toolbar) {
10875         var tcfg = this.toolbar;
10876         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
10877         this.toolbar = new Roo.Toolbar(tcfg);
10878         if (Roo.isSafari) {
10879             var tbl = tcfg.container.child('table', true);
10880             tbl.setAttribute('width', '100%');
10881         }
10882         
10883     }
10884    
10885
10886
10887     Roo.TabPanel.superclass.constructor.call(this);
10888 };
10889
10890 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
10891     /*
10892      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
10893      */
10894     tabPosition : "top",
10895     /*
10896      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
10897      */
10898     currentTabWidth : 0,
10899     /*
10900      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
10901      */
10902     minTabWidth : 40,
10903     /*
10904      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
10905      */
10906     maxTabWidth : 250,
10907     /*
10908      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
10909      */
10910     preferredTabWidth : 175,
10911     /*
10912      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
10913      */
10914     resizeTabs : false,
10915     /*
10916      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
10917      */
10918     monitorResize : true,
10919     /*
10920      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
10921      */
10922     toolbar : false,
10923
10924     /**
10925      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
10926      * @param {String} id The id of the div to use <b>or create</b>
10927      * @param {String} text The text for the tab
10928      * @param {String} content (optional) Content to put in the TabPanelItem body
10929      * @param {Boolean} closable (optional) True to create a close icon on the tab
10930      * @return {Roo.TabPanelItem} The created TabPanelItem
10931      */
10932     addTab : function(id, text, content, closable){
10933         var item = new Roo.TabPanelItem(this, id, text, closable);
10934         this.addTabItem(item);
10935         if(content){
10936             item.setContent(content);
10937         }
10938         return item;
10939     },
10940
10941     /**
10942      * Returns the {@link Roo.TabPanelItem} with the specified id/index
10943      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
10944      * @return {Roo.TabPanelItem}
10945      */
10946     getTab : function(id){
10947         return this.items[id];
10948     },
10949
10950     /**
10951      * Hides the {@link Roo.TabPanelItem} with the specified id/index
10952      * @param {String/Number} id The id or index of the TabPanelItem to hide.
10953      */
10954     hideTab : function(id){
10955         var t = this.items[id];
10956         if(!t.isHidden()){
10957            t.setHidden(true);
10958            this.hiddenCount++;
10959            this.autoSizeTabs();
10960         }
10961     },
10962
10963     /**
10964      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
10965      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
10966      */
10967     unhideTab : function(id){
10968         var t = this.items[id];
10969         if(t.isHidden()){
10970            t.setHidden(false);
10971            this.hiddenCount--;
10972            this.autoSizeTabs();
10973         }
10974     },
10975
10976     /**
10977      * Adds an existing {@link Roo.TabPanelItem}.
10978      * @param {Roo.TabPanelItem} item The TabPanelItem to add
10979      */
10980     addTabItem : function(item){
10981         this.items[item.id] = item;
10982         this.items.push(item);
10983         if(this.resizeTabs){
10984            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
10985            this.autoSizeTabs();
10986         }else{
10987             item.autoSize();
10988         }
10989     },
10990
10991     /**
10992      * Removes a {@link Roo.TabPanelItem}.
10993      * @param {String/Number} id The id or index of the TabPanelItem to remove.
10994      */
10995     removeTab : function(id){
10996         var items = this.items;
10997         var tab = items[id];
10998         if(!tab) { return; }
10999         var index = items.indexOf(tab);
11000         if(this.active == tab && items.length > 1){
11001             var newTab = this.getNextAvailable(index);
11002             if(newTab) {
11003                 newTab.activate();
11004             }
11005         }
11006         this.stripEl.dom.removeChild(tab.pnode.dom);
11007         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
11008             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
11009         }
11010         items.splice(index, 1);
11011         delete this.items[tab.id];
11012         tab.fireEvent("close", tab);
11013         tab.purgeListeners();
11014         this.autoSizeTabs();
11015     },
11016
11017     getNextAvailable : function(start){
11018         var items = this.items;
11019         var index = start;
11020         // look for a next tab that will slide over to
11021         // replace the one being removed
11022         while(index < items.length){
11023             var item = items[++index];
11024             if(item && !item.isHidden()){
11025                 return item;
11026             }
11027         }
11028         // if one isn't found select the previous tab (on the left)
11029         index = start;
11030         while(index >= 0){
11031             var item = items[--index];
11032             if(item && !item.isHidden()){
11033                 return item;
11034             }
11035         }
11036         return null;
11037     },
11038
11039     /**
11040      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
11041      * @param {String/Number} id The id or index of the TabPanelItem to disable.
11042      */
11043     disableTab : function(id){
11044         var tab = this.items[id];
11045         if(tab && this.active != tab){
11046             tab.disable();
11047         }
11048     },
11049
11050     /**
11051      * Enables a {@link Roo.TabPanelItem} that is disabled.
11052      * @param {String/Number} id The id or index of the TabPanelItem to enable.
11053      */
11054     enableTab : function(id){
11055         var tab = this.items[id];
11056         tab.enable();
11057     },
11058
11059     /**
11060      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
11061      * @param {String/Number} id The id or index of the TabPanelItem to activate.
11062      * @return {Roo.TabPanelItem} The TabPanelItem.
11063      */
11064     activate : function(id){
11065         var tab = this.items[id];
11066         if(!tab){
11067             return null;
11068         }
11069         if(tab == this.active || tab.disabled){
11070             return tab;
11071         }
11072         var e = {};
11073         this.fireEvent("beforetabchange", this, e, tab);
11074         if(e.cancel !== true && !tab.disabled){
11075             if(this.active){
11076                 this.active.hide();
11077             }
11078             this.active = this.items[id];
11079             this.active.show();
11080             this.fireEvent("tabchange", this, this.active);
11081         }
11082         return tab;
11083     },
11084
11085     /**
11086      * Gets the active {@link Roo.TabPanelItem}.
11087      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
11088      */
11089     getActiveTab : function(){
11090         return this.active;
11091     },
11092
11093     /**
11094      * Updates the tab body element to fit the height of the container element
11095      * for overflow scrolling
11096      * @param {Number} targetHeight (optional) Override the starting height from the elements height
11097      */
11098     syncHeight : function(targetHeight){
11099         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
11100         var bm = this.bodyEl.getMargins();
11101         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
11102         this.bodyEl.setHeight(newHeight);
11103         return newHeight;
11104     },
11105
11106     onResize : function(){
11107         if(this.monitorResize){
11108             this.autoSizeTabs();
11109         }
11110     },
11111
11112     /**
11113      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
11114      */
11115     beginUpdate : function(){
11116         this.updating = true;
11117     },
11118
11119     /**
11120      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
11121      */
11122     endUpdate : function(){
11123         this.updating = false;
11124         this.autoSizeTabs();
11125     },
11126
11127     /**
11128      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
11129      */
11130     autoSizeTabs : function(){
11131         var count = this.items.length;
11132         var vcount = count - this.hiddenCount;
11133         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
11134         var w = Math.max(this.el.getWidth() - this.cpad, 10);
11135         var availWidth = Math.floor(w / vcount);
11136         var b = this.stripBody;
11137         if(b.getWidth() > w){
11138             var tabs = this.items;
11139             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
11140             if(availWidth < this.minTabWidth){
11141                 /*if(!this.sleft){    // incomplete scrolling code
11142                     this.createScrollButtons();
11143                 }
11144                 this.showScroll();
11145                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
11146             }
11147         }else{
11148             if(this.currentTabWidth < this.preferredTabWidth){
11149                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
11150             }
11151         }
11152     },
11153
11154     /**
11155      * Returns the number of tabs in this TabPanel.
11156      * @return {Number}
11157      */
11158      getCount : function(){
11159          return this.items.length;
11160      },
11161
11162     /**
11163      * Resizes all the tabs to the passed width
11164      * @param {Number} The new width
11165      */
11166     setTabWidth : function(width){
11167         this.currentTabWidth = width;
11168         for(var i = 0, len = this.items.length; i < len; i++) {
11169                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
11170         }
11171     },
11172
11173     /**
11174      * Destroys this TabPanel
11175      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
11176      */
11177     destroy : function(removeEl){
11178         Roo.EventManager.removeResizeListener(this.onResize, this);
11179         for(var i = 0, len = this.items.length; i < len; i++){
11180             this.items[i].purgeListeners();
11181         }
11182         if(removeEl === true){
11183             this.el.update("");
11184             this.el.remove();
11185         }
11186     }
11187 });
11188
11189 /**
11190  * @class Roo.TabPanelItem
11191  * @extends Roo.util.Observable
11192  * Represents an individual item (tab plus body) in a TabPanel.
11193  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
11194  * @param {String} id The id of this TabPanelItem
11195  * @param {String} text The text for the tab of this TabPanelItem
11196  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
11197  */
11198 Roo.TabPanelItem = function(tabPanel, id, text, closable){
11199     /**
11200      * The {@link Roo.TabPanel} this TabPanelItem belongs to
11201      * @type Roo.TabPanel
11202      */
11203     this.tabPanel = tabPanel;
11204     /**
11205      * The id for this TabPanelItem
11206      * @type String
11207      */
11208     this.id = id;
11209     /** @private */
11210     this.disabled = false;
11211     /** @private */
11212     this.text = text;
11213     /** @private */
11214     this.loaded = false;
11215     this.closable = closable;
11216
11217     /**
11218      * The body element for this TabPanelItem.
11219      * @type Roo.Element
11220      */
11221     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
11222     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
11223     this.bodyEl.setStyle("display", "block");
11224     this.bodyEl.setStyle("zoom", "1");
11225     this.hideAction();
11226
11227     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
11228     /** @private */
11229     this.el = Roo.get(els.el, true);
11230     this.inner = Roo.get(els.inner, true);
11231     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
11232     this.pnode = Roo.get(els.el.parentNode, true);
11233     this.el.on("mousedown", this.onTabMouseDown, this);
11234     this.el.on("click", this.onTabClick, this);
11235     /** @private */
11236     if(closable){
11237         var c = Roo.get(els.close, true);
11238         c.dom.title = this.closeText;
11239         c.addClassOnOver("close-over");
11240         c.on("click", this.closeClick, this);
11241      }
11242
11243     this.addEvents({
11244          /**
11245          * @event activate
11246          * Fires when this tab becomes the active tab.
11247          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11248          * @param {Roo.TabPanelItem} this
11249          */
11250         "activate": true,
11251         /**
11252          * @event beforeclose
11253          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
11254          * @param {Roo.TabPanelItem} this
11255          * @param {Object} e Set cancel to true on this object to cancel the close.
11256          */
11257         "beforeclose": true,
11258         /**
11259          * @event close
11260          * Fires when this tab is closed.
11261          * @param {Roo.TabPanelItem} this
11262          */
11263          "close": true,
11264         /**
11265          * @event deactivate
11266          * Fires when this tab is no longer the active tab.
11267          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11268          * @param {Roo.TabPanelItem} this
11269          */
11270          "deactivate" : true
11271     });
11272     this.hidden = false;
11273
11274     Roo.TabPanelItem.superclass.constructor.call(this);
11275 };
11276
11277 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
11278     purgeListeners : function(){
11279        Roo.util.Observable.prototype.purgeListeners.call(this);
11280        this.el.removeAllListeners();
11281     },
11282     /**
11283      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
11284      */
11285     show : function(){
11286         this.pnode.addClass("on");
11287         this.showAction();
11288         if(Roo.isOpera){
11289             this.tabPanel.stripWrap.repaint();
11290         }
11291         this.fireEvent("activate", this.tabPanel, this);
11292     },
11293
11294     /**
11295      * Returns true if this tab is the active tab.
11296      * @return {Boolean}
11297      */
11298     isActive : function(){
11299         return this.tabPanel.getActiveTab() == this;
11300     },
11301
11302     /**
11303      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
11304      */
11305     hide : function(){
11306         this.pnode.removeClass("on");
11307         this.hideAction();
11308         this.fireEvent("deactivate", this.tabPanel, this);
11309     },
11310
11311     hideAction : function(){
11312         this.bodyEl.hide();
11313         this.bodyEl.setStyle("position", "absolute");
11314         this.bodyEl.setLeft("-20000px");
11315         this.bodyEl.setTop("-20000px");
11316     },
11317
11318     showAction : function(){
11319         this.bodyEl.setStyle("position", "relative");
11320         this.bodyEl.setTop("");
11321         this.bodyEl.setLeft("");
11322         this.bodyEl.show();
11323     },
11324
11325     /**
11326      * Set the tooltip for the tab.
11327      * @param {String} tooltip The tab's tooltip
11328      */
11329     setTooltip : function(text){
11330         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
11331             this.textEl.dom.qtip = text;
11332             this.textEl.dom.removeAttribute('title');
11333         }else{
11334             this.textEl.dom.title = text;
11335         }
11336     },
11337
11338     onTabClick : function(e){
11339         e.preventDefault();
11340         this.tabPanel.activate(this.id);
11341     },
11342
11343     onTabMouseDown : function(e){
11344         e.preventDefault();
11345         this.tabPanel.activate(this.id);
11346     },
11347
11348     getWidth : function(){
11349         return this.inner.getWidth();
11350     },
11351
11352     setWidth : function(width){
11353         var iwidth = width - this.pnode.getPadding("lr");
11354         this.inner.setWidth(iwidth);
11355         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
11356         this.pnode.setWidth(width);
11357     },
11358
11359     /**
11360      * Show or hide the tab
11361      * @param {Boolean} hidden True to hide or false to show.
11362      */
11363     setHidden : function(hidden){
11364         this.hidden = hidden;
11365         this.pnode.setStyle("display", hidden ? "none" : "");
11366     },
11367
11368     /**
11369      * Returns true if this tab is "hidden"
11370      * @return {Boolean}
11371      */
11372     isHidden : function(){
11373         return this.hidden;
11374     },
11375
11376     /**
11377      * Returns the text for this tab
11378      * @return {String}
11379      */
11380     getText : function(){
11381         return this.text;
11382     },
11383
11384     autoSize : function(){
11385         //this.el.beginMeasure();
11386         this.textEl.setWidth(1);
11387         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
11388         //this.el.endMeasure();
11389     },
11390
11391     /**
11392      * Sets the text for the tab (Note: this also sets the tooltip text)
11393      * @param {String} text The tab's text and tooltip
11394      */
11395     setText : function(text){
11396         this.text = text;
11397         this.textEl.update(text);
11398         this.setTooltip(text);
11399         if(!this.tabPanel.resizeTabs){
11400             this.autoSize();
11401         }
11402     },
11403     /**
11404      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
11405      */
11406     activate : function(){
11407         this.tabPanel.activate(this.id);
11408     },
11409
11410     /**
11411      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
11412      */
11413     disable : function(){
11414         if(this.tabPanel.active != this){
11415             this.disabled = true;
11416             this.pnode.addClass("disabled");
11417         }
11418     },
11419
11420     /**
11421      * Enables this TabPanelItem if it was previously disabled.
11422      */
11423     enable : function(){
11424         this.disabled = false;
11425         this.pnode.removeClass("disabled");
11426     },
11427
11428     /**
11429      * Sets the content for this TabPanelItem.
11430      * @param {String} content The content
11431      * @param {Boolean} loadScripts true to look for and load scripts
11432      */
11433     setContent : function(content, loadScripts){
11434         this.bodyEl.update(content, loadScripts);
11435     },
11436
11437     /**
11438      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
11439      * @return {Roo.UpdateManager} The UpdateManager
11440      */
11441     getUpdateManager : function(){
11442         return this.bodyEl.getUpdateManager();
11443     },
11444
11445     /**
11446      * Set a URL to be used to load the content for this TabPanelItem.
11447      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
11448      * @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)
11449      * @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)
11450      * @return {Roo.UpdateManager} The UpdateManager
11451      */
11452     setUrl : function(url, params, loadOnce){
11453         if(this.refreshDelegate){
11454             this.un('activate', this.refreshDelegate);
11455         }
11456         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
11457         this.on("activate", this.refreshDelegate);
11458         return this.bodyEl.getUpdateManager();
11459     },
11460
11461     /** @private */
11462     _handleRefresh : function(url, params, loadOnce){
11463         if(!loadOnce || !this.loaded){
11464             var updater = this.bodyEl.getUpdateManager();
11465             updater.update(url, params, this._setLoaded.createDelegate(this));
11466         }
11467     },
11468
11469     /**
11470      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
11471      *   Will fail silently if the setUrl method has not been called.
11472      *   This does not activate the panel, just updates its content.
11473      */
11474     refresh : function(){
11475         if(this.refreshDelegate){
11476            this.loaded = false;
11477            this.refreshDelegate();
11478         }
11479     },
11480
11481     /** @private */
11482     _setLoaded : function(){
11483         this.loaded = true;
11484     },
11485
11486     /** @private */
11487     closeClick : function(e){
11488         var o = {};
11489         e.stopEvent();
11490         this.fireEvent("beforeclose", this, o);
11491         if(o.cancel !== true){
11492             this.tabPanel.removeTab(this.id);
11493         }
11494     },
11495     /**
11496      * The text displayed in the tooltip for the close icon.
11497      * @type String
11498      */
11499     closeText : "Close this tab"
11500 });
11501
11502 /** @private */
11503 Roo.TabPanel.prototype.createStrip = function(container){
11504     var strip = document.createElement("div");
11505     strip.className = "x-tabs-wrap";
11506     container.appendChild(strip);
11507     return strip;
11508 };
11509 /** @private */
11510 Roo.TabPanel.prototype.createStripList = function(strip){
11511     // div wrapper for retard IE
11512     // returns the "tr" element.
11513     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
11514         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
11515         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
11516     return strip.firstChild.firstChild.firstChild.firstChild;
11517 };
11518 /** @private */
11519 Roo.TabPanel.prototype.createBody = function(container){
11520     var body = document.createElement("div");
11521     Roo.id(body, "tab-body");
11522     Roo.fly(body).addClass("x-tabs-body");
11523     container.appendChild(body);
11524     return body;
11525 };
11526 /** @private */
11527 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
11528     var body = Roo.getDom(id);
11529     if(!body){
11530         body = document.createElement("div");
11531         body.id = id;
11532     }
11533     Roo.fly(body).addClass("x-tabs-item-body");
11534     bodyEl.insertBefore(body, bodyEl.firstChild);
11535     return body;
11536 };
11537 /** @private */
11538 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
11539     var td = document.createElement("td");
11540     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
11541     //stripEl.appendChild(td);
11542     if(closable){
11543         td.className = "x-tabs-closable";
11544         if(!this.closeTpl){
11545             this.closeTpl = new Roo.Template(
11546                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11547                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
11548                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
11549             );
11550         }
11551         var el = this.closeTpl.overwrite(td, {"text": text});
11552         var close = el.getElementsByTagName("div")[0];
11553         var inner = el.getElementsByTagName("em")[0];
11554         return {"el": el, "close": close, "inner": inner};
11555     } else {
11556         if(!this.tabTpl){
11557             this.tabTpl = new Roo.Template(
11558                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11559                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
11560             );
11561         }
11562         var el = this.tabTpl.overwrite(td, {"text": text});
11563         var inner = el.getElementsByTagName("em")[0];
11564         return {"el": el, "inner": inner};
11565     }
11566 };/*
11567  * Based on:
11568  * Ext JS Library 1.1.1
11569  * Copyright(c) 2006-2007, Ext JS, LLC.
11570  *
11571  * Originally Released Under LGPL - original licence link has changed is not relivant.
11572  *
11573  * Fork - LGPL
11574  * <script type="text/javascript">
11575  */
11576
11577 /**
11578  * @class Roo.Button
11579  * @extends Roo.util.Observable
11580  * Simple Button class
11581  * @cfg {String} text The button text
11582  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11583  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11584  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11585  * @cfg {Object} scope The scope of the handler
11586  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11587  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11588  * @cfg {Boolean} hidden True to start hidden (defaults to false)
11589  * @cfg {Boolean} disabled True to start disabled (defaults to false)
11590  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11591  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11592    applies if enableToggle = true)
11593  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11594  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11595   an {@link Roo.util.ClickRepeater} config object (defaults to false).
11596  * @constructor
11597  * Create a new button
11598  * @param {Object} config The config object
11599  */
11600 Roo.Button = function(renderTo, config)
11601 {
11602     if (!config) {
11603         config = renderTo;
11604         renderTo = config.renderTo || false;
11605     }
11606     
11607     Roo.apply(this, config);
11608     this.addEvents({
11609         /**
11610              * @event click
11611              * Fires when this button is clicked
11612              * @param {Button} this
11613              * @param {EventObject} e The click event
11614              */
11615             "click" : true,
11616         /**
11617              * @event toggle
11618              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11619              * @param {Button} this
11620              * @param {Boolean} pressed
11621              */
11622             "toggle" : true,
11623         /**
11624              * @event mouseover
11625              * Fires when the mouse hovers over the button
11626              * @param {Button} this
11627              * @param {Event} e The event object
11628              */
11629         'mouseover' : true,
11630         /**
11631              * @event mouseout
11632              * Fires when the mouse exits the button
11633              * @param {Button} this
11634              * @param {Event} e The event object
11635              */
11636         'mouseout': true,
11637          /**
11638              * @event render
11639              * Fires when the button is rendered
11640              * @param {Button} this
11641              */
11642         'render': true
11643     });
11644     if(this.menu){
11645         this.menu = Roo.menu.MenuMgr.get(this.menu);
11646     }
11647     // register listeners first!!  - so render can be captured..
11648     Roo.util.Observable.call(this);
11649     if(renderTo){
11650         this.render(renderTo);
11651     }
11652     
11653   
11654 };
11655
11656 Roo.extend(Roo.Button, Roo.util.Observable, {
11657     /**
11658      * 
11659      */
11660     
11661     /**
11662      * Read-only. True if this button is hidden
11663      * @type Boolean
11664      */
11665     hidden : false,
11666     /**
11667      * Read-only. True if this button is disabled
11668      * @type Boolean
11669      */
11670     disabled : false,
11671     /**
11672      * Read-only. True if this button is pressed (only if enableToggle = true)
11673      * @type Boolean
11674      */
11675     pressed : false,
11676
11677     /**
11678      * @cfg {Number} tabIndex 
11679      * The DOM tabIndex for this button (defaults to undefined)
11680      */
11681     tabIndex : undefined,
11682
11683     /**
11684      * @cfg {Boolean} enableToggle
11685      * True to enable pressed/not pressed toggling (defaults to false)
11686      */
11687     enableToggle: false,
11688     /**
11689      * @cfg {Mixed} menu
11690      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11691      */
11692     menu : undefined,
11693     /**
11694      * @cfg {String} menuAlign
11695      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11696      */
11697     menuAlign : "tl-bl?",
11698
11699     /**
11700      * @cfg {String} iconCls
11701      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11702      */
11703     iconCls : undefined,
11704     /**
11705      * @cfg {String} type
11706      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
11707      */
11708     type : 'button',
11709
11710     // private
11711     menuClassTarget: 'tr',
11712
11713     /**
11714      * @cfg {String} clickEvent
11715      * The type of event to map to the button's event handler (defaults to 'click')
11716      */
11717     clickEvent : 'click',
11718
11719     /**
11720      * @cfg {Boolean} handleMouseEvents
11721      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11722      */
11723     handleMouseEvents : true,
11724
11725     /**
11726      * @cfg {String} tooltipType
11727      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11728      */
11729     tooltipType : 'qtip',
11730
11731     /**
11732      * @cfg {String} cls
11733      * A CSS class to apply to the button's main element.
11734      */
11735     
11736     /**
11737      * @cfg {Roo.Template} template (Optional)
11738      * An {@link Roo.Template} with which to create the Button's main element. This Template must
11739      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11740      * require code modifications if required elements (e.g. a button) aren't present.
11741      */
11742
11743     // private
11744     render : function(renderTo){
11745         var btn;
11746         if(this.hideParent){
11747             this.parentEl = Roo.get(renderTo);
11748         }
11749         if(!this.dhconfig){
11750             if(!this.template){
11751                 if(!Roo.Button.buttonTemplate){
11752                     // hideous table template
11753                     Roo.Button.buttonTemplate = new Roo.Template(
11754                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11755                         '<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>',
11756                         "</tr></tbody></table>");
11757                 }
11758                 this.template = Roo.Button.buttonTemplate;
11759             }
11760             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
11761             var btnEl = btn.child("button:first");
11762             btnEl.on('focus', this.onFocus, this);
11763             btnEl.on('blur', this.onBlur, this);
11764             if(this.cls){
11765                 btn.addClass(this.cls);
11766             }
11767             if(this.icon){
11768                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11769             }
11770             if(this.iconCls){
11771                 btnEl.addClass(this.iconCls);
11772                 if(!this.cls){
11773                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11774                 }
11775             }
11776             if(this.tabIndex !== undefined){
11777                 btnEl.dom.tabIndex = this.tabIndex;
11778             }
11779             if(this.tooltip){
11780                 if(typeof this.tooltip == 'object'){
11781                     Roo.QuickTips.tips(Roo.apply({
11782                           target: btnEl.id
11783                     }, this.tooltip));
11784                 } else {
11785                     btnEl.dom[this.tooltipType] = this.tooltip;
11786                 }
11787             }
11788         }else{
11789             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11790         }
11791         this.el = btn;
11792         if(this.id){
11793             this.el.dom.id = this.el.id = this.id;
11794         }
11795         if(this.menu){
11796             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11797             this.menu.on("show", this.onMenuShow, this);
11798             this.menu.on("hide", this.onMenuHide, this);
11799         }
11800         btn.addClass("x-btn");
11801         if(Roo.isIE && !Roo.isIE7){
11802             this.autoWidth.defer(1, this);
11803         }else{
11804             this.autoWidth();
11805         }
11806         if(this.handleMouseEvents){
11807             btn.on("mouseover", this.onMouseOver, this);
11808             btn.on("mouseout", this.onMouseOut, this);
11809             btn.on("mousedown", this.onMouseDown, this);
11810         }
11811         btn.on(this.clickEvent, this.onClick, this);
11812         //btn.on("mouseup", this.onMouseUp, this);
11813         if(this.hidden){
11814             this.hide();
11815         }
11816         if(this.disabled){
11817             this.disable();
11818         }
11819         Roo.ButtonToggleMgr.register(this);
11820         if(this.pressed){
11821             this.el.addClass("x-btn-pressed");
11822         }
11823         if(this.repeat){
11824             var repeater = new Roo.util.ClickRepeater(btn,
11825                 typeof this.repeat == "object" ? this.repeat : {}
11826             );
11827             repeater.on("click", this.onClick,  this);
11828         }
11829         
11830         this.fireEvent('render', this);
11831         
11832     },
11833     /**
11834      * Returns the button's underlying element
11835      * @return {Roo.Element} The element
11836      */
11837     getEl : function(){
11838         return this.el;  
11839     },
11840     
11841     /**
11842      * Destroys this Button and removes any listeners.
11843      */
11844     destroy : function(){
11845         Roo.ButtonToggleMgr.unregister(this);
11846         this.el.removeAllListeners();
11847         this.purgeListeners();
11848         this.el.remove();
11849     },
11850
11851     // private
11852     autoWidth : function(){
11853         if(this.el){
11854             this.el.setWidth("auto");
11855             if(Roo.isIE7 && Roo.isStrict){
11856                 var ib = this.el.child('button');
11857                 if(ib && ib.getWidth() > 20){
11858                     ib.clip();
11859                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11860                 }
11861             }
11862             if(this.minWidth){
11863                 if(this.hidden){
11864                     this.el.beginMeasure();
11865                 }
11866                 if(this.el.getWidth() < this.minWidth){
11867                     this.el.setWidth(this.minWidth);
11868                 }
11869                 if(this.hidden){
11870                     this.el.endMeasure();
11871                 }
11872             }
11873         }
11874     },
11875
11876     /**
11877      * Assigns this button's click handler
11878      * @param {Function} handler The function to call when the button is clicked
11879      * @param {Object} scope (optional) Scope for the function passed in
11880      */
11881     setHandler : function(handler, scope){
11882         this.handler = handler;
11883         this.scope = scope;  
11884     },
11885     
11886     /**
11887      * Sets this button's text
11888      * @param {String} text The button text
11889      */
11890     setText : function(text){
11891         this.text = text;
11892         if(this.el){
11893             this.el.child("td.x-btn-center button.x-btn-text").update(text);
11894         }
11895         this.autoWidth();
11896     },
11897     
11898     /**
11899      * Gets the text for this button
11900      * @return {String} The button text
11901      */
11902     getText : function(){
11903         return this.text;  
11904     },
11905     
11906     /**
11907      * Show this button
11908      */
11909     show: function(){
11910         this.hidden = false;
11911         if(this.el){
11912             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
11913         }
11914     },
11915     
11916     /**
11917      * Hide this button
11918      */
11919     hide: function(){
11920         this.hidden = true;
11921         if(this.el){
11922             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
11923         }
11924     },
11925     
11926     /**
11927      * Convenience function for boolean show/hide
11928      * @param {Boolean} visible True to show, false to hide
11929      */
11930     setVisible: function(visible){
11931         if(visible) {
11932             this.show();
11933         }else{
11934             this.hide();
11935         }
11936     },
11937     
11938     /**
11939      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
11940      * @param {Boolean} state (optional) Force a particular state
11941      */
11942     toggle : function(state){
11943         state = state === undefined ? !this.pressed : state;
11944         if(state != this.pressed){
11945             if(state){
11946                 this.el.addClass("x-btn-pressed");
11947                 this.pressed = true;
11948                 this.fireEvent("toggle", this, true);
11949             }else{
11950                 this.el.removeClass("x-btn-pressed");
11951                 this.pressed = false;
11952                 this.fireEvent("toggle", this, false);
11953             }
11954             if(this.toggleHandler){
11955                 this.toggleHandler.call(this.scope || this, this, state);
11956             }
11957         }
11958     },
11959     
11960     /**
11961      * Focus the button
11962      */
11963     focus : function(){
11964         this.el.child('button:first').focus();
11965     },
11966     
11967     /**
11968      * Disable this button
11969      */
11970     disable : function(){
11971         if(this.el){
11972             this.el.addClass("x-btn-disabled");
11973         }
11974         this.disabled = true;
11975     },
11976     
11977     /**
11978      * Enable this button
11979      */
11980     enable : function(){
11981         if(this.el){
11982             this.el.removeClass("x-btn-disabled");
11983         }
11984         this.disabled = false;
11985     },
11986
11987     /**
11988      * Convenience function for boolean enable/disable
11989      * @param {Boolean} enabled True to enable, false to disable
11990      */
11991     setDisabled : function(v){
11992         this[v !== true ? "enable" : "disable"]();
11993     },
11994
11995     // private
11996     onClick : function(e){
11997         if(e){
11998             e.preventDefault();
11999         }
12000         if(e.button != 0){
12001             return;
12002         }
12003         if(!this.disabled){
12004             if(this.enableToggle){
12005                 this.toggle();
12006             }
12007             if(this.menu && !this.menu.isVisible()){
12008                 this.menu.show(this.el, this.menuAlign);
12009             }
12010             this.fireEvent("click", this, e);
12011             if(this.handler){
12012                 this.el.removeClass("x-btn-over");
12013                 this.handler.call(this.scope || this, this, e);
12014             }
12015         }
12016     },
12017     // private
12018     onMouseOver : function(e){
12019         if(!this.disabled){
12020             this.el.addClass("x-btn-over");
12021             this.fireEvent('mouseover', this, e);
12022         }
12023     },
12024     // private
12025     onMouseOut : function(e){
12026         if(!e.within(this.el,  true)){
12027             this.el.removeClass("x-btn-over");
12028             this.fireEvent('mouseout', this, e);
12029         }
12030     },
12031     // private
12032     onFocus : function(e){
12033         if(!this.disabled){
12034             this.el.addClass("x-btn-focus");
12035         }
12036     },
12037     // private
12038     onBlur : function(e){
12039         this.el.removeClass("x-btn-focus");
12040     },
12041     // private
12042     onMouseDown : function(e){
12043         if(!this.disabled && e.button == 0){
12044             this.el.addClass("x-btn-click");
12045             Roo.get(document).on('mouseup', this.onMouseUp, this);
12046         }
12047     },
12048     // private
12049     onMouseUp : function(e){
12050         if(e.button == 0){
12051             this.el.removeClass("x-btn-click");
12052             Roo.get(document).un('mouseup', this.onMouseUp, this);
12053         }
12054     },
12055     // private
12056     onMenuShow : function(e){
12057         this.el.addClass("x-btn-menu-active");
12058     },
12059     // private
12060     onMenuHide : function(e){
12061         this.el.removeClass("x-btn-menu-active");
12062     }   
12063 });
12064
12065 // Private utility class used by Button
12066 Roo.ButtonToggleMgr = function(){
12067    var groups = {};
12068    
12069    function toggleGroup(btn, state){
12070        if(state){
12071            var g = groups[btn.toggleGroup];
12072            for(var i = 0, l = g.length; i < l; i++){
12073                if(g[i] != btn){
12074                    g[i].toggle(false);
12075                }
12076            }
12077        }
12078    }
12079    
12080    return {
12081        register : function(btn){
12082            if(!btn.toggleGroup){
12083                return;
12084            }
12085            var g = groups[btn.toggleGroup];
12086            if(!g){
12087                g = groups[btn.toggleGroup] = [];
12088            }
12089            g.push(btn);
12090            btn.on("toggle", toggleGroup);
12091        },
12092        
12093        unregister : function(btn){
12094            if(!btn.toggleGroup){
12095                return;
12096            }
12097            var g = groups[btn.toggleGroup];
12098            if(g){
12099                g.remove(btn);
12100                btn.un("toggle", toggleGroup);
12101            }
12102        }
12103    };
12104 }();/*
12105  * Based on:
12106  * Ext JS Library 1.1.1
12107  * Copyright(c) 2006-2007, Ext JS, LLC.
12108  *
12109  * Originally Released Under LGPL - original licence link has changed is not relivant.
12110  *
12111  * Fork - LGPL
12112  * <script type="text/javascript">
12113  */
12114  
12115 /**
12116  * @class Roo.SplitButton
12117  * @extends Roo.Button
12118  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
12119  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
12120  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
12121  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
12122  * @cfg {String} arrowTooltip The title attribute of the arrow
12123  * @constructor
12124  * Create a new menu button
12125  * @param {String/HTMLElement/Element} renderTo The element to append the button to
12126  * @param {Object} config The config object
12127  */
12128 Roo.SplitButton = function(renderTo, config){
12129     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
12130     /**
12131      * @event arrowclick
12132      * Fires when this button's arrow is clicked
12133      * @param {SplitButton} this
12134      * @param {EventObject} e The click event
12135      */
12136     this.addEvents({"arrowclick":true});
12137 };
12138
12139 Roo.extend(Roo.SplitButton, Roo.Button, {
12140     render : function(renderTo){
12141         // this is one sweet looking template!
12142         var tpl = new Roo.Template(
12143             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
12144             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
12145             '<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>',
12146             "</tbody></table></td><td>",
12147             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
12148             '<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>',
12149             "</tbody></table></td></tr></table>"
12150         );
12151         var btn = tpl.append(renderTo, [this.text, this.type], true);
12152         var btnEl = btn.child("button");
12153         if(this.cls){
12154             btn.addClass(this.cls);
12155         }
12156         if(this.icon){
12157             btnEl.setStyle('background-image', 'url(' +this.icon +')');
12158         }
12159         if(this.iconCls){
12160             btnEl.addClass(this.iconCls);
12161             if(!this.cls){
12162                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
12163             }
12164         }
12165         this.el = btn;
12166         if(this.handleMouseEvents){
12167             btn.on("mouseover", this.onMouseOver, this);
12168             btn.on("mouseout", this.onMouseOut, this);
12169             btn.on("mousedown", this.onMouseDown, this);
12170             btn.on("mouseup", this.onMouseUp, this);
12171         }
12172         btn.on(this.clickEvent, this.onClick, this);
12173         if(this.tooltip){
12174             if(typeof this.tooltip == 'object'){
12175                 Roo.QuickTips.tips(Roo.apply({
12176                       target: btnEl.id
12177                 }, this.tooltip));
12178             } else {
12179                 btnEl.dom[this.tooltipType] = this.tooltip;
12180             }
12181         }
12182         if(this.arrowTooltip){
12183             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
12184         }
12185         if(this.hidden){
12186             this.hide();
12187         }
12188         if(this.disabled){
12189             this.disable();
12190         }
12191         if(this.pressed){
12192             this.el.addClass("x-btn-pressed");
12193         }
12194         if(Roo.isIE && !Roo.isIE7){
12195             this.autoWidth.defer(1, this);
12196         }else{
12197             this.autoWidth();
12198         }
12199         if(this.menu){
12200             this.menu.on("show", this.onMenuShow, this);
12201             this.menu.on("hide", this.onMenuHide, this);
12202         }
12203         this.fireEvent('render', this);
12204     },
12205
12206     // private
12207     autoWidth : function(){
12208         if(this.el){
12209             var tbl = this.el.child("table:first");
12210             var tbl2 = this.el.child("table:last");
12211             this.el.setWidth("auto");
12212             tbl.setWidth("auto");
12213             if(Roo.isIE7 && Roo.isStrict){
12214                 var ib = this.el.child('button:first');
12215                 if(ib && ib.getWidth() > 20){
12216                     ib.clip();
12217                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
12218                 }
12219             }
12220             if(this.minWidth){
12221                 if(this.hidden){
12222                     this.el.beginMeasure();
12223                 }
12224                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
12225                     tbl.setWidth(this.minWidth-tbl2.getWidth());
12226                 }
12227                 if(this.hidden){
12228                     this.el.endMeasure();
12229                 }
12230             }
12231             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
12232         } 
12233     },
12234     /**
12235      * Sets this button's click handler
12236      * @param {Function} handler The function to call when the button is clicked
12237      * @param {Object} scope (optional) Scope for the function passed above
12238      */
12239     setHandler : function(handler, scope){
12240         this.handler = handler;
12241         this.scope = scope;  
12242     },
12243     
12244     /**
12245      * Sets this button's arrow click handler
12246      * @param {Function} handler The function to call when the arrow is clicked
12247      * @param {Object} scope (optional) Scope for the function passed above
12248      */
12249     setArrowHandler : function(handler, scope){
12250         this.arrowHandler = handler;
12251         this.scope = scope;  
12252     },
12253     
12254     /**
12255      * Focus the button
12256      */
12257     focus : function(){
12258         if(this.el){
12259             this.el.child("button:first").focus();
12260         }
12261     },
12262
12263     // private
12264     onClick : function(e){
12265         e.preventDefault();
12266         if(!this.disabled){
12267             if(e.getTarget(".x-btn-menu-arrow-wrap")){
12268                 if(this.menu && !this.menu.isVisible()){
12269                     this.menu.show(this.el, this.menuAlign);
12270                 }
12271                 this.fireEvent("arrowclick", this, e);
12272                 if(this.arrowHandler){
12273                     this.arrowHandler.call(this.scope || this, this, e);
12274                 }
12275             }else{
12276                 this.fireEvent("click", this, e);
12277                 if(this.handler){
12278                     this.handler.call(this.scope || this, this, e);
12279                 }
12280             }
12281         }
12282     },
12283     // private
12284     onMouseDown : function(e){
12285         if(!this.disabled){
12286             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
12287         }
12288     },
12289     // private
12290     onMouseUp : function(e){
12291         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
12292     }   
12293 });
12294
12295
12296 // backwards compat
12297 Roo.MenuButton = Roo.SplitButton;/*
12298  * Based on:
12299  * Ext JS Library 1.1.1
12300  * Copyright(c) 2006-2007, Ext JS, LLC.
12301  *
12302  * Originally Released Under LGPL - original licence link has changed is not relivant.
12303  *
12304  * Fork - LGPL
12305  * <script type="text/javascript">
12306  */
12307
12308 /**
12309  * @class Roo.Toolbar
12310  * Basic Toolbar class.
12311  * @constructor
12312  * Creates a new Toolbar
12313  * @param {Object} container The config object
12314  */ 
12315 Roo.Toolbar = function(container, buttons, config)
12316 {
12317     /// old consturctor format still supported..
12318     if(container instanceof Array){ // omit the container for later rendering
12319         buttons = container;
12320         config = buttons;
12321         container = null;
12322     }
12323     if (typeof(container) == 'object' && container.xtype) {
12324         config = container;
12325         container = config.container;
12326         buttons = config.buttons || []; // not really - use items!!
12327     }
12328     var xitems = [];
12329     if (config && config.items) {
12330         xitems = config.items;
12331         delete config.items;
12332     }
12333     Roo.apply(this, config);
12334     this.buttons = buttons;
12335     
12336     if(container){
12337         this.render(container);
12338     }
12339     this.xitems = xitems;
12340     Roo.each(xitems, function(b) {
12341         this.add(b);
12342     }, this);
12343     
12344 };
12345
12346 Roo.Toolbar.prototype = {
12347     /**
12348      * @cfg {Array} items
12349      * array of button configs or elements to add (will be converted to a MixedCollection)
12350      */
12351     
12352     /**
12353      * @cfg {String/HTMLElement/Element} container
12354      * The id or element that will contain the toolbar
12355      */
12356     // private
12357     render : function(ct){
12358         this.el = Roo.get(ct);
12359         if(this.cls){
12360             this.el.addClass(this.cls);
12361         }
12362         // using a table allows for vertical alignment
12363         // 100% width is needed by Safari...
12364         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
12365         this.tr = this.el.child("tr", true);
12366         var autoId = 0;
12367         this.items = new Roo.util.MixedCollection(false, function(o){
12368             return o.id || ("item" + (++autoId));
12369         });
12370         if(this.buttons){
12371             this.add.apply(this, this.buttons);
12372             delete this.buttons;
12373         }
12374     },
12375
12376     /**
12377      * Adds element(s) to the toolbar -- this function takes a variable number of 
12378      * arguments of mixed type and adds them to the toolbar.
12379      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
12380      * <ul>
12381      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
12382      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
12383      * <li>Field: Any form field (equivalent to {@link #addField})</li>
12384      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
12385      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
12386      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
12387      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
12388      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
12389      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
12390      * </ul>
12391      * @param {Mixed} arg2
12392      * @param {Mixed} etc.
12393      */
12394     add : function(){
12395         var a = arguments, l = a.length;
12396         for(var i = 0; i < l; i++){
12397             this._add(a[i]);
12398         }
12399     },
12400     // private..
12401     _add : function(el) {
12402         
12403         if (el.xtype) {
12404             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
12405         }
12406         
12407         if (el.applyTo){ // some kind of form field
12408             return this.addField(el);
12409         } 
12410         if (el.render){ // some kind of Toolbar.Item
12411             return this.addItem(el);
12412         }
12413         if (typeof el == "string"){ // string
12414             if(el == "separator" || el == "-"){
12415                 return this.addSeparator();
12416             }
12417             if (el == " "){
12418                 return this.addSpacer();
12419             }
12420             if(el == "->"){
12421                 return this.addFill();
12422             }
12423             return this.addText(el);
12424             
12425         }
12426         if(el.tagName){ // element
12427             return this.addElement(el);
12428         }
12429         if(typeof el == "object"){ // must be button config?
12430             return this.addButton(el);
12431         }
12432         // and now what?!?!
12433         return false;
12434         
12435     },
12436     
12437     /**
12438      * Add an Xtype element
12439      * @param {Object} xtype Xtype Object
12440      * @return {Object} created Object
12441      */
12442     addxtype : function(e){
12443         return this.add(e);  
12444     },
12445     
12446     /**
12447      * Returns the Element for this toolbar.
12448      * @return {Roo.Element}
12449      */
12450     getEl : function(){
12451         return this.el;  
12452     },
12453     
12454     /**
12455      * Adds a separator
12456      * @return {Roo.Toolbar.Item} The separator item
12457      */
12458     addSeparator : function(){
12459         return this.addItem(new Roo.Toolbar.Separator());
12460     },
12461
12462     /**
12463      * Adds a spacer element
12464      * @return {Roo.Toolbar.Spacer} The spacer item
12465      */
12466     addSpacer : function(){
12467         return this.addItem(new Roo.Toolbar.Spacer());
12468     },
12469
12470     /**
12471      * Adds a fill element that forces subsequent additions to the right side of the toolbar
12472      * @return {Roo.Toolbar.Fill} The fill item
12473      */
12474     addFill : function(){
12475         return this.addItem(new Roo.Toolbar.Fill());
12476     },
12477
12478     /**
12479      * Adds any standard HTML element to the toolbar
12480      * @param {String/HTMLElement/Element} el The element or id of the element to add
12481      * @return {Roo.Toolbar.Item} The element's item
12482      */
12483     addElement : function(el){
12484         return this.addItem(new Roo.Toolbar.Item(el));
12485     },
12486     /**
12487      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
12488      * @type Roo.util.MixedCollection  
12489      */
12490     items : false,
12491      
12492     /**
12493      * Adds any Toolbar.Item or subclass
12494      * @param {Roo.Toolbar.Item} item
12495      * @return {Roo.Toolbar.Item} The item
12496      */
12497     addItem : function(item){
12498         var td = this.nextBlock();
12499         item.render(td);
12500         this.items.add(item);
12501         return item;
12502     },
12503     
12504     /**
12505      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
12506      * @param {Object/Array} config A button config or array of configs
12507      * @return {Roo.Toolbar.Button/Array}
12508      */
12509     addButton : function(config){
12510         if(config instanceof Array){
12511             var buttons = [];
12512             for(var i = 0, len = config.length; i < len; i++) {
12513                 buttons.push(this.addButton(config[i]));
12514             }
12515             return buttons;
12516         }
12517         var b = config;
12518         if(!(config instanceof Roo.Toolbar.Button)){
12519             b = config.split ?
12520                 new Roo.Toolbar.SplitButton(config) :
12521                 new Roo.Toolbar.Button(config);
12522         }
12523         var td = this.nextBlock();
12524         b.render(td);
12525         this.items.add(b);
12526         return b;
12527     },
12528     
12529     /**
12530      * Adds text to the toolbar
12531      * @param {String} text The text to add
12532      * @return {Roo.Toolbar.Item} The element's item
12533      */
12534     addText : function(text){
12535         return this.addItem(new Roo.Toolbar.TextItem(text));
12536     },
12537     
12538     /**
12539      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
12540      * @param {Number} index The index where the item is to be inserted
12541      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
12542      * @return {Roo.Toolbar.Button/Item}
12543      */
12544     insertButton : function(index, item){
12545         if(item instanceof Array){
12546             var buttons = [];
12547             for(var i = 0, len = item.length; i < len; i++) {
12548                buttons.push(this.insertButton(index + i, item[i]));
12549             }
12550             return buttons;
12551         }
12552         if (!(item instanceof Roo.Toolbar.Button)){
12553            item = new Roo.Toolbar.Button(item);
12554         }
12555         var td = document.createElement("td");
12556         this.tr.insertBefore(td, this.tr.childNodes[index]);
12557         item.render(td);
12558         this.items.insert(index, item);
12559         return item;
12560     },
12561     
12562     /**
12563      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12564      * @param {Object} config
12565      * @return {Roo.Toolbar.Item} The element's item
12566      */
12567     addDom : function(config, returnEl){
12568         var td = this.nextBlock();
12569         Roo.DomHelper.overwrite(td, config);
12570         var ti = new Roo.Toolbar.Item(td.firstChild);
12571         ti.render(td);
12572         this.items.add(ti);
12573         return ti;
12574     },
12575
12576     /**
12577      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12578      * @type Roo.util.MixedCollection  
12579      */
12580     fields : false,
12581     
12582     /**
12583      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
12584      * Note: the field should not have been rendered yet. For a field that has already been
12585      * rendered, use {@link #addElement}.
12586      * @param {Roo.form.Field} field
12587      * @return {Roo.ToolbarItem}
12588      */
12589      
12590       
12591     addField : function(field) {
12592         if (!this.fields) {
12593             var autoId = 0;
12594             this.fields = new Roo.util.MixedCollection(false, function(o){
12595                 return o.id || ("item" + (++autoId));
12596             });
12597
12598         }
12599         
12600         var td = this.nextBlock();
12601         field.render(td);
12602         var ti = new Roo.Toolbar.Item(td.firstChild);
12603         ti.render(td);
12604         this.items.add(ti);
12605         this.fields.add(field);
12606         return ti;
12607     },
12608     /**
12609      * Hide the toolbar
12610      * @method hide
12611      */
12612      
12613       
12614     hide : function()
12615     {
12616         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12617         this.el.child('div').hide();
12618     },
12619     /**
12620      * Show the toolbar
12621      * @method show
12622      */
12623     show : function()
12624     {
12625         this.el.child('div').show();
12626     },
12627       
12628     // private
12629     nextBlock : function(){
12630         var td = document.createElement("td");
12631         this.tr.appendChild(td);
12632         return td;
12633     },
12634
12635     // private
12636     destroy : function(){
12637         if(this.items){ // rendered?
12638             Roo.destroy.apply(Roo, this.items.items);
12639         }
12640         if(this.fields){ // rendered?
12641             Roo.destroy.apply(Roo, this.fields.items);
12642         }
12643         Roo.Element.uncache(this.el, this.tr);
12644     }
12645 };
12646
12647 /**
12648  * @class Roo.Toolbar.Item
12649  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12650  * @constructor
12651  * Creates a new Item
12652  * @param {HTMLElement} el 
12653  */
12654 Roo.Toolbar.Item = function(el){
12655     this.el = Roo.getDom(el);
12656     this.id = Roo.id(this.el);
12657     this.hidden = false;
12658 };
12659
12660 Roo.Toolbar.Item.prototype = {
12661     
12662     /**
12663      * Get this item's HTML Element
12664      * @return {HTMLElement}
12665      */
12666     getEl : function(){
12667        return this.el;  
12668     },
12669
12670     // private
12671     render : function(td){
12672         this.td = td;
12673         td.appendChild(this.el);
12674     },
12675     
12676     /**
12677      * Removes and destroys this item.
12678      */
12679     destroy : function(){
12680         this.td.parentNode.removeChild(this.td);
12681     },
12682     
12683     /**
12684      * Shows this item.
12685      */
12686     show: function(){
12687         this.hidden = false;
12688         this.td.style.display = "";
12689     },
12690     
12691     /**
12692      * Hides this item.
12693      */
12694     hide: function(){
12695         this.hidden = true;
12696         this.td.style.display = "none";
12697     },
12698     
12699     /**
12700      * Convenience function for boolean show/hide.
12701      * @param {Boolean} visible true to show/false to hide
12702      */
12703     setVisible: function(visible){
12704         if(visible) {
12705             this.show();
12706         }else{
12707             this.hide();
12708         }
12709     },
12710     
12711     /**
12712      * Try to focus this item.
12713      */
12714     focus : function(){
12715         Roo.fly(this.el).focus();
12716     },
12717     
12718     /**
12719      * Disables this item.
12720      */
12721     disable : function(){
12722         Roo.fly(this.td).addClass("x-item-disabled");
12723         this.disabled = true;
12724         this.el.disabled = true;
12725     },
12726     
12727     /**
12728      * Enables this item.
12729      */
12730     enable : function(){
12731         Roo.fly(this.td).removeClass("x-item-disabled");
12732         this.disabled = false;
12733         this.el.disabled = false;
12734     }
12735 };
12736
12737
12738 /**
12739  * @class Roo.Toolbar.Separator
12740  * @extends Roo.Toolbar.Item
12741  * A simple toolbar separator class
12742  * @constructor
12743  * Creates a new Separator
12744  */
12745 Roo.Toolbar.Separator = function(){
12746     var s = document.createElement("span");
12747     s.className = "ytb-sep";
12748     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
12749 };
12750 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12751     enable:Roo.emptyFn,
12752     disable:Roo.emptyFn,
12753     focus:Roo.emptyFn
12754 });
12755
12756 /**
12757  * @class Roo.Toolbar.Spacer
12758  * @extends Roo.Toolbar.Item
12759  * A simple element that adds extra horizontal space to a toolbar.
12760  * @constructor
12761  * Creates a new Spacer
12762  */
12763 Roo.Toolbar.Spacer = function(){
12764     var s = document.createElement("div");
12765     s.className = "ytb-spacer";
12766     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
12767 };
12768 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12769     enable:Roo.emptyFn,
12770     disable:Roo.emptyFn,
12771     focus:Roo.emptyFn
12772 });
12773
12774 /**
12775  * @class Roo.Toolbar.Fill
12776  * @extends Roo.Toolbar.Spacer
12777  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12778  * @constructor
12779  * Creates a new Spacer
12780  */
12781 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12782     // private
12783     render : function(td){
12784         td.style.width = '100%';
12785         Roo.Toolbar.Fill.superclass.render.call(this, td);
12786     }
12787 });
12788
12789 /**
12790  * @class Roo.Toolbar.TextItem
12791  * @extends Roo.Toolbar.Item
12792  * A simple class that renders text directly into a toolbar.
12793  * @constructor
12794  * Creates a new TextItem
12795  * @param {String} text
12796  */
12797 Roo.Toolbar.TextItem = function(text){
12798     if (typeof(text) == 'object') {
12799         text = text.text;
12800     }
12801     var s = document.createElement("span");
12802     s.className = "ytb-text";
12803     s.innerHTML = text;
12804     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
12805 };
12806 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12807     enable:Roo.emptyFn,
12808     disable:Roo.emptyFn,
12809     focus:Roo.emptyFn
12810 });
12811
12812 /**
12813  * @class Roo.Toolbar.Button
12814  * @extends Roo.Button
12815  * A button that renders into a toolbar.
12816  * @constructor
12817  * Creates a new Button
12818  * @param {Object} config A standard {@link Roo.Button} config object
12819  */
12820 Roo.Toolbar.Button = function(config){
12821     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12822 };
12823 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12824     render : function(td){
12825         this.td = td;
12826         Roo.Toolbar.Button.superclass.render.call(this, td);
12827     },
12828     
12829     /**
12830      * Removes and destroys this button
12831      */
12832     destroy : function(){
12833         Roo.Toolbar.Button.superclass.destroy.call(this);
12834         this.td.parentNode.removeChild(this.td);
12835     },
12836     
12837     /**
12838      * Shows this button
12839      */
12840     show: function(){
12841         this.hidden = false;
12842         this.td.style.display = "";
12843     },
12844     
12845     /**
12846      * Hides this button
12847      */
12848     hide: function(){
12849         this.hidden = true;
12850         this.td.style.display = "none";
12851     },
12852
12853     /**
12854      * Disables this item
12855      */
12856     disable : function(){
12857         Roo.fly(this.td).addClass("x-item-disabled");
12858         this.disabled = true;
12859     },
12860
12861     /**
12862      * Enables this item
12863      */
12864     enable : function(){
12865         Roo.fly(this.td).removeClass("x-item-disabled");
12866         this.disabled = false;
12867     }
12868 });
12869 // backwards compat
12870 Roo.ToolbarButton = Roo.Toolbar.Button;
12871
12872 /**
12873  * @class Roo.Toolbar.SplitButton
12874  * @extends Roo.SplitButton
12875  * A menu button that renders into a toolbar.
12876  * @constructor
12877  * Creates a new SplitButton
12878  * @param {Object} config A standard {@link Roo.SplitButton} config object
12879  */
12880 Roo.Toolbar.SplitButton = function(config){
12881     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
12882 };
12883 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
12884     render : function(td){
12885         this.td = td;
12886         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
12887     },
12888     
12889     /**
12890      * Removes and destroys this button
12891      */
12892     destroy : function(){
12893         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
12894         this.td.parentNode.removeChild(this.td);
12895     },
12896     
12897     /**
12898      * Shows this button
12899      */
12900     show: function(){
12901         this.hidden = false;
12902         this.td.style.display = "";
12903     },
12904     
12905     /**
12906      * Hides this button
12907      */
12908     hide: function(){
12909         this.hidden = true;
12910         this.td.style.display = "none";
12911     }
12912 });
12913
12914 // backwards compat
12915 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
12916  * Based on:
12917  * Ext JS Library 1.1.1
12918  * Copyright(c) 2006-2007, Ext JS, LLC.
12919  *
12920  * Originally Released Under LGPL - original licence link has changed is not relivant.
12921  *
12922  * Fork - LGPL
12923  * <script type="text/javascript">
12924  */
12925  
12926 /**
12927  * @class Roo.PagingToolbar
12928  * @extends Roo.Toolbar
12929  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
12930  * @constructor
12931  * Create a new PagingToolbar
12932  * @param {Object} config The config object
12933  */
12934 Roo.PagingToolbar = function(el, ds, config)
12935 {
12936     // old args format still supported... - xtype is prefered..
12937     if (typeof(el) == 'object' && el.xtype) {
12938         // created from xtype...
12939         config = el;
12940         ds = el.dataSource;
12941         el = config.container;
12942     }
12943     var items = [];
12944     if (config.items) {
12945         items = config.items;
12946         config.items = [];
12947     }
12948     
12949     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
12950     this.ds = ds;
12951     this.cursor = 0;
12952     this.renderButtons(this.el);
12953     this.bind(ds);
12954     
12955     // supprot items array.
12956    
12957     Roo.each(items, function(e) {
12958         this.add(Roo.factory(e));
12959     },this);
12960     
12961 };
12962
12963 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
12964     /**
12965      * @cfg {Roo.data.Store} dataSource
12966      * The underlying data store providing the paged data
12967      */
12968     /**
12969      * @cfg {String/HTMLElement/Element} container
12970      * container The id or element that will contain the toolbar
12971      */
12972     /**
12973      * @cfg {Boolean} displayInfo
12974      * True to display the displayMsg (defaults to false)
12975      */
12976     /**
12977      * @cfg {Number} pageSize
12978      * The number of records to display per page (defaults to 20)
12979      */
12980     pageSize: 20,
12981     /**
12982      * @cfg {String} displayMsg
12983      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
12984      */
12985     displayMsg : 'Displaying {0} - {1} of {2}',
12986     /**
12987      * @cfg {String} emptyMsg
12988      * The message to display when no records are found (defaults to "No data to display")
12989      */
12990     emptyMsg : 'No data to display',
12991     /**
12992      * Customizable piece of the default paging text (defaults to "Page")
12993      * @type String
12994      */
12995     beforePageText : "Page",
12996     /**
12997      * Customizable piece of the default paging text (defaults to "of %0")
12998      * @type String
12999      */
13000     afterPageText : "of {0}",
13001     /**
13002      * Customizable piece of the default paging text (defaults to "First Page")
13003      * @type String
13004      */
13005     firstText : "First Page",
13006     /**
13007      * Customizable piece of the default paging text (defaults to "Previous Page")
13008      * @type String
13009      */
13010     prevText : "Previous Page",
13011     /**
13012      * Customizable piece of the default paging text (defaults to "Next Page")
13013      * @type String
13014      */
13015     nextText : "Next Page",
13016     /**
13017      * Customizable piece of the default paging text (defaults to "Last Page")
13018      * @type String
13019      */
13020     lastText : "Last Page",
13021     /**
13022      * Customizable piece of the default paging text (defaults to "Refresh")
13023      * @type String
13024      */
13025     refreshText : "Refresh",
13026
13027     // private
13028     renderButtons : function(el){
13029         Roo.PagingToolbar.superclass.render.call(this, el);
13030         this.first = this.addButton({
13031             tooltip: this.firstText,
13032             cls: "x-btn-icon x-grid-page-first",
13033             disabled: true,
13034             handler: this.onClick.createDelegate(this, ["first"])
13035         });
13036         this.prev = this.addButton({
13037             tooltip: this.prevText,
13038             cls: "x-btn-icon x-grid-page-prev",
13039             disabled: true,
13040             handler: this.onClick.createDelegate(this, ["prev"])
13041         });
13042         //this.addSeparator();
13043         this.add(this.beforePageText);
13044         this.field = Roo.get(this.addDom({
13045            tag: "input",
13046            type: "text",
13047            size: "3",
13048            value: "1",
13049            cls: "x-grid-page-number"
13050         }).el);
13051         this.field.on("keydown", this.onPagingKeydown, this);
13052         this.field.on("focus", function(){this.dom.select();});
13053         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
13054         this.field.setHeight(18);
13055         //this.addSeparator();
13056         this.next = this.addButton({
13057             tooltip: this.nextText,
13058             cls: "x-btn-icon x-grid-page-next",
13059             disabled: true,
13060             handler: this.onClick.createDelegate(this, ["next"])
13061         });
13062         this.last = this.addButton({
13063             tooltip: this.lastText,
13064             cls: "x-btn-icon x-grid-page-last",
13065             disabled: true,
13066             handler: this.onClick.createDelegate(this, ["last"])
13067         });
13068         //this.addSeparator();
13069         this.loading = this.addButton({
13070             tooltip: this.refreshText,
13071             cls: "x-btn-icon x-grid-loading",
13072             handler: this.onClick.createDelegate(this, ["refresh"])
13073         });
13074
13075         if(this.displayInfo){
13076             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
13077         }
13078     },
13079
13080     // private
13081     updateInfo : function(){
13082         if(this.displayEl){
13083             var count = this.ds.getCount();
13084             var msg = count == 0 ?
13085                 this.emptyMsg :
13086                 String.format(
13087                     this.displayMsg,
13088                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
13089                 );
13090             this.displayEl.update(msg);
13091         }
13092     },
13093
13094     // private
13095     onLoad : function(ds, r, o){
13096        this.cursor = o.params ? o.params.start : 0;
13097        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
13098
13099        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
13100        this.field.dom.value = ap;
13101        this.first.setDisabled(ap == 1);
13102        this.prev.setDisabled(ap == 1);
13103        this.next.setDisabled(ap == ps);
13104        this.last.setDisabled(ap == ps);
13105        this.loading.enable();
13106        this.updateInfo();
13107     },
13108
13109     // private
13110     getPageData : function(){
13111         var total = this.ds.getTotalCount();
13112         return {
13113             total : total,
13114             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
13115             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
13116         };
13117     },
13118
13119     // private
13120     onLoadError : function(){
13121         this.loading.enable();
13122     },
13123
13124     // private
13125     onPagingKeydown : function(e){
13126         var k = e.getKey();
13127         var d = this.getPageData();
13128         if(k == e.RETURN){
13129             var v = this.field.dom.value, pageNum;
13130             if(!v || isNaN(pageNum = parseInt(v, 10))){
13131                 this.field.dom.value = d.activePage;
13132                 return;
13133             }
13134             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
13135             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13136             e.stopEvent();
13137         }
13138         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))
13139         {
13140           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
13141           this.field.dom.value = pageNum;
13142           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
13143           e.stopEvent();
13144         }
13145         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13146         {
13147           var v = this.field.dom.value, pageNum; 
13148           var increment = (e.shiftKey) ? 10 : 1;
13149           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13150             increment *= -1;
13151           if(!v || isNaN(pageNum = parseInt(v, 10))) {
13152             this.field.dom.value = d.activePage;
13153             return;
13154           }
13155           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
13156           {
13157             this.field.dom.value = parseInt(v, 10) + increment;
13158             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
13159             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13160           }
13161           e.stopEvent();
13162         }
13163     },
13164
13165     // private
13166     beforeLoad : function(){
13167         if(this.loading){
13168             this.loading.disable();
13169         }
13170     },
13171
13172     // private
13173     onClick : function(which){
13174         var ds = this.ds;
13175         switch(which){
13176             case "first":
13177                 ds.load({params:{start: 0, limit: this.pageSize}});
13178             break;
13179             case "prev":
13180                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
13181             break;
13182             case "next":
13183                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
13184             break;
13185             case "last":
13186                 var total = ds.getTotalCount();
13187                 var extra = total % this.pageSize;
13188                 var lastStart = extra ? (total - extra) : total-this.pageSize;
13189                 ds.load({params:{start: lastStart, limit: this.pageSize}});
13190             break;
13191             case "refresh":
13192                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
13193             break;
13194         }
13195     },
13196
13197     /**
13198      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
13199      * @param {Roo.data.Store} store The data store to unbind
13200      */
13201     unbind : function(ds){
13202         ds.un("beforeload", this.beforeLoad, this);
13203         ds.un("load", this.onLoad, this);
13204         ds.un("loadexception", this.onLoadError, this);
13205         ds.un("remove", this.updateInfo, this);
13206         ds.un("add", this.updateInfo, this);
13207         this.ds = undefined;
13208     },
13209
13210     /**
13211      * Binds the paging toolbar to the specified {@link Roo.data.Store}
13212      * @param {Roo.data.Store} store The data store to bind
13213      */
13214     bind : function(ds){
13215         ds.on("beforeload", this.beforeLoad, this);
13216         ds.on("load", this.onLoad, this);
13217         ds.on("loadexception", this.onLoadError, this);
13218         ds.on("remove", this.updateInfo, this);
13219         ds.on("add", this.updateInfo, this);
13220         this.ds = ds;
13221     }
13222 });/*
13223  * Based on:
13224  * Ext JS Library 1.1.1
13225  * Copyright(c) 2006-2007, Ext JS, LLC.
13226  *
13227  * Originally Released Under LGPL - original licence link has changed is not relivant.
13228  *
13229  * Fork - LGPL
13230  * <script type="text/javascript">
13231  */
13232
13233 /**
13234  * @class Roo.Resizable
13235  * @extends Roo.util.Observable
13236  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
13237  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
13238  * 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
13239  * the element will be wrapped for you automatically.</p>
13240  * <p>Here is the list of valid resize handles:</p>
13241  * <pre>
13242 Value   Description
13243 ------  -------------------
13244  'n'     north
13245  's'     south
13246  'e'     east
13247  'w'     west
13248  'nw'    northwest
13249  'sw'    southwest
13250  'se'    southeast
13251  'ne'    northeast
13252  'hd'    horizontal drag
13253  'all'   all
13254 </pre>
13255  * <p>Here's an example showing the creation of a typical Resizable:</p>
13256  * <pre><code>
13257 var resizer = new Roo.Resizable("element-id", {
13258     handles: 'all',
13259     minWidth: 200,
13260     minHeight: 100,
13261     maxWidth: 500,
13262     maxHeight: 400,
13263     pinned: true
13264 });
13265 resizer.on("resize", myHandler);
13266 </code></pre>
13267  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
13268  * resizer.east.setDisplayed(false);</p>
13269  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
13270  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
13271  * resize operation's new size (defaults to [0, 0])
13272  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
13273  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
13274  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
13275  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
13276  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
13277  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
13278  * @cfg {Number} width The width of the element in pixels (defaults to null)
13279  * @cfg {Number} height The height of the element in pixels (defaults to null)
13280  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
13281  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
13282  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
13283  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
13284  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
13285  * in favor of the handles config option (defaults to false)
13286  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
13287  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
13288  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
13289  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
13290  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
13291  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
13292  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
13293  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
13294  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
13295  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
13296  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
13297  * @constructor
13298  * Create a new resizable component
13299  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
13300  * @param {Object} config configuration options
13301   */
13302 Roo.Resizable = function(el, config)
13303 {
13304     this.el = Roo.get(el);
13305
13306     if(config && config.wrap){
13307         config.resizeChild = this.el;
13308         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
13309         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
13310         this.el.setStyle("overflow", "hidden");
13311         this.el.setPositioning(config.resizeChild.getPositioning());
13312         config.resizeChild.clearPositioning();
13313         if(!config.width || !config.height){
13314             var csize = config.resizeChild.getSize();
13315             this.el.setSize(csize.width, csize.height);
13316         }
13317         if(config.pinned && !config.adjustments){
13318             config.adjustments = "auto";
13319         }
13320     }
13321
13322     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
13323     this.proxy.unselectable();
13324     this.proxy.enableDisplayMode('block');
13325
13326     Roo.apply(this, config);
13327
13328     if(this.pinned){
13329         this.disableTrackOver = true;
13330         this.el.addClass("x-resizable-pinned");
13331     }
13332     // if the element isn't positioned, make it relative
13333     var position = this.el.getStyle("position");
13334     if(position != "absolute" && position != "fixed"){
13335         this.el.setStyle("position", "relative");
13336     }
13337     if(!this.handles){ // no handles passed, must be legacy style
13338         this.handles = 's,e,se';
13339         if(this.multiDirectional){
13340             this.handles += ',n,w';
13341         }
13342     }
13343     if(this.handles == "all"){
13344         this.handles = "n s e w ne nw se sw";
13345     }
13346     var hs = this.handles.split(/\s*?[,;]\s*?| /);
13347     var ps = Roo.Resizable.positions;
13348     for(var i = 0, len = hs.length; i < len; i++){
13349         if(hs[i] && ps[hs[i]]){
13350             var pos = ps[hs[i]];
13351             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
13352         }
13353     }
13354     // legacy
13355     this.corner = this.southeast;
13356     
13357     // updateBox = the box can move..
13358     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
13359         this.updateBox = true;
13360     }
13361
13362     this.activeHandle = null;
13363
13364     if(this.resizeChild){
13365         if(typeof this.resizeChild == "boolean"){
13366             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
13367         }else{
13368             this.resizeChild = Roo.get(this.resizeChild, true);
13369         }
13370     }
13371     
13372     if(this.adjustments == "auto"){
13373         var rc = this.resizeChild;
13374         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
13375         if(rc && (hw || hn)){
13376             rc.position("relative");
13377             rc.setLeft(hw ? hw.el.getWidth() : 0);
13378             rc.setTop(hn ? hn.el.getHeight() : 0);
13379         }
13380         this.adjustments = [
13381             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
13382             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
13383         ];
13384     }
13385
13386     if(this.draggable){
13387         this.dd = this.dynamic ?
13388             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
13389         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
13390     }
13391
13392     // public events
13393     this.addEvents({
13394         /**
13395          * @event beforeresize
13396          * Fired before resize is allowed. Set enabled to false to cancel resize.
13397          * @param {Roo.Resizable} this
13398          * @param {Roo.EventObject} e The mousedown event
13399          */
13400         "beforeresize" : true,
13401         /**
13402          * @event resize
13403          * Fired after a resize.
13404          * @param {Roo.Resizable} this
13405          * @param {Number} width The new width
13406          * @param {Number} height The new height
13407          * @param {Roo.EventObject} e The mouseup event
13408          */
13409         "resize" : true
13410     });
13411
13412     if(this.width !== null && this.height !== null){
13413         this.resizeTo(this.width, this.height);
13414     }else{
13415         this.updateChildSize();
13416     }
13417     if(Roo.isIE){
13418         this.el.dom.style.zoom = 1;
13419     }
13420     Roo.Resizable.superclass.constructor.call(this);
13421 };
13422
13423 Roo.extend(Roo.Resizable, Roo.util.Observable, {
13424         resizeChild : false,
13425         adjustments : [0, 0],
13426         minWidth : 5,
13427         minHeight : 5,
13428         maxWidth : 10000,
13429         maxHeight : 10000,
13430         enabled : true,
13431         animate : false,
13432         duration : .35,
13433         dynamic : false,
13434         handles : false,
13435         multiDirectional : false,
13436         disableTrackOver : false,
13437         easing : 'easeOutStrong',
13438         widthIncrement : 0,
13439         heightIncrement : 0,
13440         pinned : false,
13441         width : null,
13442         height : null,
13443         preserveRatio : false,
13444         transparent: false,
13445         minX: 0,
13446         minY: 0,
13447         draggable: false,
13448
13449         /**
13450          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
13451          */
13452         constrainTo: undefined,
13453         /**
13454          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
13455          */
13456         resizeRegion: undefined,
13457
13458
13459     /**
13460      * Perform a manual resize
13461      * @param {Number} width
13462      * @param {Number} height
13463      */
13464     resizeTo : function(width, height){
13465         this.el.setSize(width, height);
13466         this.updateChildSize();
13467         this.fireEvent("resize", this, width, height, null);
13468     },
13469
13470     // private
13471     startSizing : function(e, handle){
13472         this.fireEvent("beforeresize", this, e);
13473         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
13474
13475             if(!this.overlay){
13476                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
13477                 this.overlay.unselectable();
13478                 this.overlay.enableDisplayMode("block");
13479                 this.overlay.on("mousemove", this.onMouseMove, this);
13480                 this.overlay.on("mouseup", this.onMouseUp, this);
13481             }
13482             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
13483
13484             this.resizing = true;
13485             this.startBox = this.el.getBox();
13486             this.startPoint = e.getXY();
13487             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
13488                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
13489
13490             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13491             this.overlay.show();
13492
13493             if(this.constrainTo) {
13494                 var ct = Roo.get(this.constrainTo);
13495                 this.resizeRegion = ct.getRegion().adjust(
13496                     ct.getFrameWidth('t'),
13497                     ct.getFrameWidth('l'),
13498                     -ct.getFrameWidth('b'),
13499                     -ct.getFrameWidth('r')
13500                 );
13501             }
13502
13503             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
13504             this.proxy.show();
13505             this.proxy.setBox(this.startBox);
13506             if(!this.dynamic){
13507                 this.proxy.setStyle('visibility', 'visible');
13508             }
13509         }
13510     },
13511
13512     // private
13513     onMouseDown : function(handle, e){
13514         if(this.enabled){
13515             e.stopEvent();
13516             this.activeHandle = handle;
13517             this.startSizing(e, handle);
13518         }
13519     },
13520
13521     // private
13522     onMouseUp : function(e){
13523         var size = this.resizeElement();
13524         this.resizing = false;
13525         this.handleOut();
13526         this.overlay.hide();
13527         this.proxy.hide();
13528         this.fireEvent("resize", this, size.width, size.height, e);
13529     },
13530
13531     // private
13532     updateChildSize : function(){
13533         if(this.resizeChild){
13534             var el = this.el;
13535             var child = this.resizeChild;
13536             var adj = this.adjustments;
13537             if(el.dom.offsetWidth){
13538                 var b = el.getSize(true);
13539                 child.setSize(b.width+adj[0], b.height+adj[1]);
13540             }
13541             // Second call here for IE
13542             // The first call enables instant resizing and
13543             // the second call corrects scroll bars if they
13544             // exist
13545             if(Roo.isIE){
13546                 setTimeout(function(){
13547                     if(el.dom.offsetWidth){
13548                         var b = el.getSize(true);
13549                         child.setSize(b.width+adj[0], b.height+adj[1]);
13550                     }
13551                 }, 10);
13552             }
13553         }
13554     },
13555
13556     // private
13557     snap : function(value, inc, min){
13558         if(!inc || !value) return value;
13559         var newValue = value;
13560         var m = value % inc;
13561         if(m > 0){
13562             if(m > (inc/2)){
13563                 newValue = value + (inc-m);
13564             }else{
13565                 newValue = value - m;
13566             }
13567         }
13568         return Math.max(min, newValue);
13569     },
13570
13571     // private
13572     resizeElement : function(){
13573         var box = this.proxy.getBox();
13574         if(this.updateBox){
13575             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13576         }else{
13577             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13578         }
13579         this.updateChildSize();
13580         if(!this.dynamic){
13581             this.proxy.hide();
13582         }
13583         return box;
13584     },
13585
13586     // private
13587     constrain : function(v, diff, m, mx){
13588         if(v - diff < m){
13589             diff = v - m;
13590         }else if(v - diff > mx){
13591             diff = mx - v;
13592         }
13593         return diff;
13594     },
13595
13596     // private
13597     onMouseMove : function(e){
13598         if(this.enabled){
13599             try{// try catch so if something goes wrong the user doesn't get hung
13600
13601             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13602                 return;
13603             }
13604
13605             //var curXY = this.startPoint;
13606             var curSize = this.curSize || this.startBox;
13607             var x = this.startBox.x, y = this.startBox.y;
13608             var ox = x, oy = y;
13609             var w = curSize.width, h = curSize.height;
13610             var ow = w, oh = h;
13611             var mw = this.minWidth, mh = this.minHeight;
13612             var mxw = this.maxWidth, mxh = this.maxHeight;
13613             var wi = this.widthIncrement;
13614             var hi = this.heightIncrement;
13615
13616             var eventXY = e.getXY();
13617             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13618             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13619
13620             var pos = this.activeHandle.position;
13621
13622             switch(pos){
13623                 case "east":
13624                     w += diffX;
13625                     w = Math.min(Math.max(mw, w), mxw);
13626                     break;
13627              
13628                 case "south":
13629                     h += diffY;
13630                     h = Math.min(Math.max(mh, h), mxh);
13631                     break;
13632                 case "southeast":
13633                     w += diffX;
13634                     h += diffY;
13635                     w = Math.min(Math.max(mw, w), mxw);
13636                     h = Math.min(Math.max(mh, h), mxh);
13637                     break;
13638                 case "north":
13639                     diffY = this.constrain(h, diffY, mh, mxh);
13640                     y += diffY;
13641                     h -= diffY;
13642                     break;
13643                 case "hdrag":
13644                     
13645                     if (wi) {
13646                         var adiffX = Math.abs(diffX);
13647                         var sub = (adiffX % wi); // how much 
13648                         if (sub > (wi/2)) { // far enough to snap
13649                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
13650                         } else {
13651                             // remove difference.. 
13652                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
13653                         }
13654                     }
13655                     x += diffX;
13656                     x = Math.max(this.minX, x);
13657                     break;
13658                 case "west":
13659                     diffX = this.constrain(w, diffX, mw, mxw);
13660                     x += diffX;
13661                     w -= diffX;
13662                     break;
13663                 case "northeast":
13664                     w += diffX;
13665                     w = Math.min(Math.max(mw, w), mxw);
13666                     diffY = this.constrain(h, diffY, mh, mxh);
13667                     y += diffY;
13668                     h -= diffY;
13669                     break;
13670                 case "northwest":
13671                     diffX = this.constrain(w, diffX, mw, mxw);
13672                     diffY = this.constrain(h, diffY, mh, mxh);
13673                     y += diffY;
13674                     h -= diffY;
13675                     x += diffX;
13676                     w -= diffX;
13677                     break;
13678                case "southwest":
13679                     diffX = this.constrain(w, diffX, mw, mxw);
13680                     h += diffY;
13681                     h = Math.min(Math.max(mh, h), mxh);
13682                     x += diffX;
13683                     w -= diffX;
13684                     break;
13685             }
13686
13687             var sw = this.snap(w, wi, mw);
13688             var sh = this.snap(h, hi, mh);
13689             if(sw != w || sh != h){
13690                 switch(pos){
13691                     case "northeast":
13692                         y -= sh - h;
13693                     break;
13694                     case "north":
13695                         y -= sh - h;
13696                         break;
13697                     case "southwest":
13698                         x -= sw - w;
13699                     break;
13700                     case "west":
13701                         x -= sw - w;
13702                         break;
13703                     case "northwest":
13704                         x -= sw - w;
13705                         y -= sh - h;
13706                     break;
13707                 }
13708                 w = sw;
13709                 h = sh;
13710             }
13711
13712             if(this.preserveRatio){
13713                 switch(pos){
13714                     case "southeast":
13715                     case "east":
13716                         h = oh * (w/ow);
13717                         h = Math.min(Math.max(mh, h), mxh);
13718                         w = ow * (h/oh);
13719                        break;
13720                     case "south":
13721                         w = ow * (h/oh);
13722                         w = Math.min(Math.max(mw, w), mxw);
13723                         h = oh * (w/ow);
13724                         break;
13725                     case "northeast":
13726                         w = ow * (h/oh);
13727                         w = Math.min(Math.max(mw, w), mxw);
13728                         h = oh * (w/ow);
13729                     break;
13730                     case "north":
13731                         var tw = w;
13732                         w = ow * (h/oh);
13733                         w = Math.min(Math.max(mw, w), mxw);
13734                         h = oh * (w/ow);
13735                         x += (tw - w) / 2;
13736                         break;
13737                     case "southwest":
13738                         h = oh * (w/ow);
13739                         h = Math.min(Math.max(mh, h), mxh);
13740                         var tw = w;
13741                         w = ow * (h/oh);
13742                         x += tw - w;
13743                         break;
13744                     case "west":
13745                         var th = h;
13746                         h = oh * (w/ow);
13747                         h = Math.min(Math.max(mh, h), mxh);
13748                         y += (th - h) / 2;
13749                         var tw = w;
13750                         w = ow * (h/oh);
13751                         x += tw - w;
13752                        break;
13753                     case "northwest":
13754                         var tw = w;
13755                         var th = h;
13756                         h = oh * (w/ow);
13757                         h = Math.min(Math.max(mh, h), mxh);
13758                         w = ow * (h/oh);
13759                         y += th - h;
13760                         x += tw - w;
13761                        break;
13762
13763                 }
13764             }
13765             if (pos == 'hdrag') {
13766                 w = ow;
13767             }
13768             this.proxy.setBounds(x, y, w, h);
13769             if(this.dynamic){
13770                 this.resizeElement();
13771             }
13772             }catch(e){}
13773         }
13774     },
13775
13776     // private
13777     handleOver : function(){
13778         if(this.enabled){
13779             this.el.addClass("x-resizable-over");
13780         }
13781     },
13782
13783     // private
13784     handleOut : function(){
13785         if(!this.resizing){
13786             this.el.removeClass("x-resizable-over");
13787         }
13788     },
13789
13790     /**
13791      * Returns the element this component is bound to.
13792      * @return {Roo.Element}
13793      */
13794     getEl : function(){
13795         return this.el;
13796     },
13797
13798     /**
13799      * Returns the resizeChild element (or null).
13800      * @return {Roo.Element}
13801      */
13802     getResizeChild : function(){
13803         return this.resizeChild;
13804     },
13805
13806     /**
13807      * Destroys this resizable. If the element was wrapped and
13808      * removeEl is not true then the element remains.
13809      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13810      */
13811     destroy : function(removeEl){
13812         this.proxy.remove();
13813         if(this.overlay){
13814             this.overlay.removeAllListeners();
13815             this.overlay.remove();
13816         }
13817         var ps = Roo.Resizable.positions;
13818         for(var k in ps){
13819             if(typeof ps[k] != "function" && this[ps[k]]){
13820                 var h = this[ps[k]];
13821                 h.el.removeAllListeners();
13822                 h.el.remove();
13823             }
13824         }
13825         if(removeEl){
13826             this.el.update("");
13827             this.el.remove();
13828         }
13829     }
13830 });
13831
13832 // private
13833 // hash to map config positions to true positions
13834 Roo.Resizable.positions = {
13835     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
13836     hd: "hdrag"
13837 };
13838
13839 // private
13840 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
13841     if(!this.tpl){
13842         // only initialize the template if resizable is used
13843         var tpl = Roo.DomHelper.createTemplate(
13844             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
13845         );
13846         tpl.compile();
13847         Roo.Resizable.Handle.prototype.tpl = tpl;
13848     }
13849     this.position = pos;
13850     this.rz = rz;
13851     // show north drag fro topdra
13852     var handlepos = pos == 'hdrag' ? 'north' : pos;
13853     
13854     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
13855     if (pos == 'hdrag') {
13856         this.el.setStyle('cursor', 'pointer');
13857     }
13858     this.el.unselectable();
13859     if(transparent){
13860         this.el.setOpacity(0);
13861     }
13862     this.el.on("mousedown", this.onMouseDown, this);
13863     if(!disableTrackOver){
13864         this.el.on("mouseover", this.onMouseOver, this);
13865         this.el.on("mouseout", this.onMouseOut, this);
13866     }
13867 };
13868
13869 // private
13870 Roo.Resizable.Handle.prototype = {
13871     afterResize : function(rz){
13872         // do nothing
13873     },
13874     // private
13875     onMouseDown : function(e){
13876         this.rz.onMouseDown(this, e);
13877     },
13878     // private
13879     onMouseOver : function(e){
13880         this.rz.handleOver(this, e);
13881     },
13882     // private
13883     onMouseOut : function(e){
13884         this.rz.handleOut(this, e);
13885     }
13886 };/*
13887  * Based on:
13888  * Ext JS Library 1.1.1
13889  * Copyright(c) 2006-2007, Ext JS, LLC.
13890  *
13891  * Originally Released Under LGPL - original licence link has changed is not relivant.
13892  *
13893  * Fork - LGPL
13894  * <script type="text/javascript">
13895  */
13896
13897 /**
13898  * @class Roo.Editor
13899  * @extends Roo.Component
13900  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
13901  * @constructor
13902  * Create a new Editor
13903  * @param {Roo.form.Field} field The Field object (or descendant)
13904  * @param {Object} config The config object
13905  */
13906 Roo.Editor = function(field, config){
13907     Roo.Editor.superclass.constructor.call(this, config);
13908     this.field = field;
13909     this.addEvents({
13910         /**
13911              * @event beforestartedit
13912              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
13913              * false from the handler of this event.
13914              * @param {Editor} this
13915              * @param {Roo.Element} boundEl The underlying element bound to this editor
13916              * @param {Mixed} value The field value being set
13917              */
13918         "beforestartedit" : true,
13919         /**
13920              * @event startedit
13921              * Fires when this editor is displayed
13922              * @param {Roo.Element} boundEl The underlying element bound to this editor
13923              * @param {Mixed} value The starting field value
13924              */
13925         "startedit" : true,
13926         /**
13927              * @event beforecomplete
13928              * Fires after a change has been made to the field, but before the change is reflected in the underlying
13929              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
13930              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
13931              * event will not fire since no edit actually occurred.
13932              * @param {Editor} this
13933              * @param {Mixed} value The current field value
13934              * @param {Mixed} startValue The original field value
13935              */
13936         "beforecomplete" : true,
13937         /**
13938              * @event complete
13939              * Fires after editing is complete and any changed value has been written to the underlying field.
13940              * @param {Editor} this
13941              * @param {Mixed} value The current field value
13942              * @param {Mixed} startValue The original field value
13943              */
13944         "complete" : true,
13945         /**
13946          * @event specialkey
13947          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
13948          * {@link Roo.EventObject#getKey} to determine which key was pressed.
13949          * @param {Roo.form.Field} this
13950          * @param {Roo.EventObject} e The event object
13951          */
13952         "specialkey" : true
13953     });
13954 };
13955
13956 Roo.extend(Roo.Editor, Roo.Component, {
13957     /**
13958      * @cfg {Boolean/String} autosize
13959      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
13960      * or "height" to adopt the height only (defaults to false)
13961      */
13962     /**
13963      * @cfg {Boolean} revertInvalid
13964      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
13965      * validation fails (defaults to true)
13966      */
13967     /**
13968      * @cfg {Boolean} ignoreNoChange
13969      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
13970      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
13971      * will never be ignored.
13972      */
13973     /**
13974      * @cfg {Boolean} hideEl
13975      * False to keep the bound element visible while the editor is displayed (defaults to true)
13976      */
13977     /**
13978      * @cfg {Mixed} value
13979      * The data value of the underlying field (defaults to "")
13980      */
13981     value : "",
13982     /**
13983      * @cfg {String} alignment
13984      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
13985      */
13986     alignment: "c-c?",
13987     /**
13988      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
13989      * for bottom-right shadow (defaults to "frame")
13990      */
13991     shadow : "frame",
13992     /**
13993      * @cfg {Boolean} constrain True to constrain the editor to the viewport
13994      */
13995     constrain : false,
13996     /**
13997      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
13998      */
13999     completeOnEnter : false,
14000     /**
14001      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
14002      */
14003     cancelOnEsc : false,
14004     /**
14005      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
14006      */
14007     updateEl : false,
14008
14009     // private
14010     onRender : function(ct, position){
14011         this.el = new Roo.Layer({
14012             shadow: this.shadow,
14013             cls: "x-editor",
14014             parentEl : ct,
14015             shim : this.shim,
14016             shadowOffset:4,
14017             id: this.id,
14018             constrain: this.constrain
14019         });
14020         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
14021         if(this.field.msgTarget != 'title'){
14022             this.field.msgTarget = 'qtip';
14023         }
14024         this.field.render(this.el);
14025         if(Roo.isGecko){
14026             this.field.el.dom.setAttribute('autocomplete', 'off');
14027         }
14028         this.field.on("specialkey", this.onSpecialKey, this);
14029         if(this.swallowKeys){
14030             this.field.el.swallowEvent(['keydown','keypress']);
14031         }
14032         this.field.show();
14033         this.field.on("blur", this.onBlur, this);
14034         if(this.field.grow){
14035             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
14036         }
14037     },
14038
14039     onSpecialKey : function(field, e)
14040     {
14041         //Roo.log('editor onSpecialKey');
14042         if(this.completeOnEnter && e.getKey() == e.ENTER){
14043             e.stopEvent();
14044             this.completeEdit();
14045             return;
14046         }
14047         // do not fire special key otherwise it might hide close the editor...
14048         if(e.getKey() == e.ENTER){    
14049             return;
14050         }
14051         if(this.cancelOnEsc && e.getKey() == e.ESC){
14052             this.cancelEdit();
14053             return;
14054         } 
14055         this.fireEvent('specialkey', field, e);
14056     
14057     },
14058
14059     /**
14060      * Starts the editing process and shows the editor.
14061      * @param {String/HTMLElement/Element} el The element to edit
14062      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
14063       * to the innerHTML of el.
14064      */
14065     startEdit : function(el, value){
14066         if(this.editing){
14067             this.completeEdit();
14068         }
14069         this.boundEl = Roo.get(el);
14070         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
14071         if(!this.rendered){
14072             this.render(this.parentEl || document.body);
14073         }
14074         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
14075             return;
14076         }
14077         this.startValue = v;
14078         this.field.setValue(v);
14079         if(this.autoSize){
14080             var sz = this.boundEl.getSize();
14081             switch(this.autoSize){
14082                 case "width":
14083                 this.setSize(sz.width,  "");
14084                 break;
14085                 case "height":
14086                 this.setSize("",  sz.height);
14087                 break;
14088                 default:
14089                 this.setSize(sz.width,  sz.height);
14090             }
14091         }
14092         this.el.alignTo(this.boundEl, this.alignment);
14093         this.editing = true;
14094         if(Roo.QuickTips){
14095             Roo.QuickTips.disable();
14096         }
14097         this.show();
14098     },
14099
14100     /**
14101      * Sets the height and width of this editor.
14102      * @param {Number} width The new width
14103      * @param {Number} height The new height
14104      */
14105     setSize : function(w, h){
14106         this.field.setSize(w, h);
14107         if(this.el){
14108             this.el.sync();
14109         }
14110     },
14111
14112     /**
14113      * Realigns the editor to the bound field based on the current alignment config value.
14114      */
14115     realign : function(){
14116         this.el.alignTo(this.boundEl, this.alignment);
14117     },
14118
14119     /**
14120      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
14121      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
14122      */
14123     completeEdit : function(remainVisible){
14124         if(!this.editing){
14125             return;
14126         }
14127         var v = this.getValue();
14128         if(this.revertInvalid !== false && !this.field.isValid()){
14129             v = this.startValue;
14130             this.cancelEdit(true);
14131         }
14132         if(String(v) === String(this.startValue) && this.ignoreNoChange){
14133             this.editing = false;
14134             this.hide();
14135             return;
14136         }
14137         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
14138             this.editing = false;
14139             if(this.updateEl && this.boundEl){
14140                 this.boundEl.update(v);
14141             }
14142             if(remainVisible !== true){
14143                 this.hide();
14144             }
14145             this.fireEvent("complete", this, v, this.startValue);
14146         }
14147     },
14148
14149     // private
14150     onShow : function(){
14151         this.el.show();
14152         if(this.hideEl !== false){
14153             this.boundEl.hide();
14154         }
14155         this.field.show();
14156         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
14157             this.fixIEFocus = true;
14158             this.deferredFocus.defer(50, this);
14159         }else{
14160             this.field.focus();
14161         }
14162         this.fireEvent("startedit", this.boundEl, this.startValue);
14163     },
14164
14165     deferredFocus : function(){
14166         if(this.editing){
14167             this.field.focus();
14168         }
14169     },
14170
14171     /**
14172      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
14173      * reverted to the original starting value.
14174      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
14175      * cancel (defaults to false)
14176      */
14177     cancelEdit : function(remainVisible){
14178         if(this.editing){
14179             this.setValue(this.startValue);
14180             if(remainVisible !== true){
14181                 this.hide();
14182             }
14183         }
14184     },
14185
14186     // private
14187     onBlur : function(){
14188         if(this.allowBlur !== true && this.editing){
14189             this.completeEdit();
14190         }
14191     },
14192
14193     // private
14194     onHide : function(){
14195         if(this.editing){
14196             this.completeEdit();
14197             return;
14198         }
14199         this.field.blur();
14200         if(this.field.collapse){
14201             this.field.collapse();
14202         }
14203         this.el.hide();
14204         if(this.hideEl !== false){
14205             this.boundEl.show();
14206         }
14207         if(Roo.QuickTips){
14208             Roo.QuickTips.enable();
14209         }
14210     },
14211
14212     /**
14213      * Sets the data value of the editor
14214      * @param {Mixed} value Any valid value supported by the underlying field
14215      */
14216     setValue : function(v){
14217         this.field.setValue(v);
14218     },
14219
14220     /**
14221      * Gets the data value of the editor
14222      * @return {Mixed} The data value
14223      */
14224     getValue : function(){
14225         return this.field.getValue();
14226     }
14227 });/*
14228  * Based on:
14229  * Ext JS Library 1.1.1
14230  * Copyright(c) 2006-2007, Ext JS, LLC.
14231  *
14232  * Originally Released Under LGPL - original licence link has changed is not relivant.
14233  *
14234  * Fork - LGPL
14235  * <script type="text/javascript">
14236  */
14237  
14238 /**
14239  * @class Roo.BasicDialog
14240  * @extends Roo.util.Observable
14241  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
14242  * <pre><code>
14243 var dlg = new Roo.BasicDialog("my-dlg", {
14244     height: 200,
14245     width: 300,
14246     minHeight: 100,
14247     minWidth: 150,
14248     modal: true,
14249     proxyDrag: true,
14250     shadow: true
14251 });
14252 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
14253 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
14254 dlg.addButton('Cancel', dlg.hide, dlg);
14255 dlg.show();
14256 </code></pre>
14257   <b>A Dialog should always be a direct child of the body element.</b>
14258  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
14259  * @cfg {String} title Default text to display in the title bar (defaults to null)
14260  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14261  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14262  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
14263  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
14264  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
14265  * (defaults to null with no animation)
14266  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
14267  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
14268  * property for valid values (defaults to 'all')
14269  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
14270  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
14271  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
14272  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
14273  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
14274  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
14275  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
14276  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
14277  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
14278  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
14279  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
14280  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
14281  * draggable = true (defaults to false)
14282  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
14283  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14284  * shadow (defaults to false)
14285  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
14286  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
14287  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
14288  * @cfg {Array} buttons Array of buttons
14289  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
14290  * @constructor
14291  * Create a new BasicDialog.
14292  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
14293  * @param {Object} config Configuration options
14294  */
14295 Roo.BasicDialog = function(el, config){
14296     this.el = Roo.get(el);
14297     var dh = Roo.DomHelper;
14298     if(!this.el && config && config.autoCreate){
14299         if(typeof config.autoCreate == "object"){
14300             if(!config.autoCreate.id){
14301                 config.autoCreate.id = el;
14302             }
14303             this.el = dh.append(document.body,
14304                         config.autoCreate, true);
14305         }else{
14306             this.el = dh.append(document.body,
14307                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
14308         }
14309     }
14310     el = this.el;
14311     el.setDisplayed(true);
14312     el.hide = this.hideAction;
14313     this.id = el.id;
14314     el.addClass("x-dlg");
14315
14316     Roo.apply(this, config);
14317
14318     this.proxy = el.createProxy("x-dlg-proxy");
14319     this.proxy.hide = this.hideAction;
14320     this.proxy.setOpacity(.5);
14321     this.proxy.hide();
14322
14323     if(config.width){
14324         el.setWidth(config.width);
14325     }
14326     if(config.height){
14327         el.setHeight(config.height);
14328     }
14329     this.size = el.getSize();
14330     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
14331         this.xy = [config.x,config.y];
14332     }else{
14333         this.xy = el.getCenterXY(true);
14334     }
14335     /** The header element @type Roo.Element */
14336     this.header = el.child("> .x-dlg-hd");
14337     /** The body element @type Roo.Element */
14338     this.body = el.child("> .x-dlg-bd");
14339     /** The footer element @type Roo.Element */
14340     this.footer = el.child("> .x-dlg-ft");
14341
14342     if(!this.header){
14343         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
14344     }
14345     if(!this.body){
14346         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
14347     }
14348
14349     this.header.unselectable();
14350     if(this.title){
14351         this.header.update(this.title);
14352     }
14353     // this element allows the dialog to be focused for keyboard event
14354     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
14355     this.focusEl.swallowEvent("click", true);
14356
14357     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
14358
14359     // wrap the body and footer for special rendering
14360     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
14361     if(this.footer){
14362         this.bwrap.dom.appendChild(this.footer.dom);
14363     }
14364
14365     this.bg = this.el.createChild({
14366         tag: "div", cls:"x-dlg-bg",
14367         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
14368     });
14369     this.centerBg = this.bg.child("div.x-dlg-bg-center");
14370
14371
14372     if(this.autoScroll !== false && !this.autoTabs){
14373         this.body.setStyle("overflow", "auto");
14374     }
14375
14376     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
14377
14378     if(this.closable !== false){
14379         this.el.addClass("x-dlg-closable");
14380         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
14381         this.close.on("click", this.closeClick, this);
14382         this.close.addClassOnOver("x-dlg-close-over");
14383     }
14384     if(this.collapsible !== false){
14385         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
14386         this.collapseBtn.on("click", this.collapseClick, this);
14387         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
14388         this.header.on("dblclick", this.collapseClick, this);
14389     }
14390     if(this.resizable !== false){
14391         this.el.addClass("x-dlg-resizable");
14392         this.resizer = new Roo.Resizable(el, {
14393             minWidth: this.minWidth || 80,
14394             minHeight:this.minHeight || 80,
14395             handles: this.resizeHandles || "all",
14396             pinned: true
14397         });
14398         this.resizer.on("beforeresize", this.beforeResize, this);
14399         this.resizer.on("resize", this.onResize, this);
14400     }
14401     if(this.draggable !== false){
14402         el.addClass("x-dlg-draggable");
14403         if (!this.proxyDrag) {
14404             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
14405         }
14406         else {
14407             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
14408         }
14409         dd.setHandleElId(this.header.id);
14410         dd.endDrag = this.endMove.createDelegate(this);
14411         dd.startDrag = this.startMove.createDelegate(this);
14412         dd.onDrag = this.onDrag.createDelegate(this);
14413         dd.scroll = false;
14414         this.dd = dd;
14415     }
14416     if(this.modal){
14417         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
14418         this.mask.enableDisplayMode("block");
14419         this.mask.hide();
14420         this.el.addClass("x-dlg-modal");
14421     }
14422     if(this.shadow){
14423         this.shadow = new Roo.Shadow({
14424             mode : typeof this.shadow == "string" ? this.shadow : "sides",
14425             offset : this.shadowOffset
14426         });
14427     }else{
14428         this.shadowOffset = 0;
14429     }
14430     if(Roo.useShims && this.shim !== false){
14431         this.shim = this.el.createShim();
14432         this.shim.hide = this.hideAction;
14433         this.shim.hide();
14434     }else{
14435         this.shim = false;
14436     }
14437     if(this.autoTabs){
14438         this.initTabs();
14439     }
14440     if (this.buttons) { 
14441         var bts= this.buttons;
14442         this.buttons = [];
14443         Roo.each(bts, function(b) {
14444             this.addButton(b);
14445         }, this);
14446     }
14447     
14448     
14449     this.addEvents({
14450         /**
14451          * @event keydown
14452          * Fires when a key is pressed
14453          * @param {Roo.BasicDialog} this
14454          * @param {Roo.EventObject} e
14455          */
14456         "keydown" : true,
14457         /**
14458          * @event move
14459          * Fires when this dialog is moved by the user.
14460          * @param {Roo.BasicDialog} this
14461          * @param {Number} x The new page X
14462          * @param {Number} y The new page Y
14463          */
14464         "move" : true,
14465         /**
14466          * @event resize
14467          * Fires when this dialog is resized by the user.
14468          * @param {Roo.BasicDialog} this
14469          * @param {Number} width The new width
14470          * @param {Number} height The new height
14471          */
14472         "resize" : true,
14473         /**
14474          * @event beforehide
14475          * Fires before this dialog is hidden.
14476          * @param {Roo.BasicDialog} this
14477          */
14478         "beforehide" : true,
14479         /**
14480          * @event hide
14481          * Fires when this dialog is hidden.
14482          * @param {Roo.BasicDialog} this
14483          */
14484         "hide" : true,
14485         /**
14486          * @event beforeshow
14487          * Fires before this dialog is shown.
14488          * @param {Roo.BasicDialog} this
14489          */
14490         "beforeshow" : true,
14491         /**
14492          * @event show
14493          * Fires when this dialog is shown.
14494          * @param {Roo.BasicDialog} this
14495          */
14496         "show" : true
14497     });
14498     el.on("keydown", this.onKeyDown, this);
14499     el.on("mousedown", this.toFront, this);
14500     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
14501     this.el.hide();
14502     Roo.DialogManager.register(this);
14503     Roo.BasicDialog.superclass.constructor.call(this);
14504 };
14505
14506 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
14507     shadowOffset: Roo.isIE ? 6 : 5,
14508     minHeight: 80,
14509     minWidth: 200,
14510     minButtonWidth: 75,
14511     defaultButton: null,
14512     buttonAlign: "right",
14513     tabTag: 'div',
14514     firstShow: true,
14515
14516     /**
14517      * Sets the dialog title text
14518      * @param {String} text The title text to display
14519      * @return {Roo.BasicDialog} this
14520      */
14521     setTitle : function(text){
14522         this.header.update(text);
14523         return this;
14524     },
14525
14526     // private
14527     closeClick : function(){
14528         this.hide();
14529     },
14530
14531     // private
14532     collapseClick : function(){
14533         this[this.collapsed ? "expand" : "collapse"]();
14534     },
14535
14536     /**
14537      * Collapses the dialog to its minimized state (only the title bar is visible).
14538      * Equivalent to the user clicking the collapse dialog button.
14539      */
14540     collapse : function(){
14541         if(!this.collapsed){
14542             this.collapsed = true;
14543             this.el.addClass("x-dlg-collapsed");
14544             this.restoreHeight = this.el.getHeight();
14545             this.resizeTo(this.el.getWidth(), this.header.getHeight());
14546         }
14547     },
14548
14549     /**
14550      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
14551      * clicking the expand dialog button.
14552      */
14553     expand : function(){
14554         if(this.collapsed){
14555             this.collapsed = false;
14556             this.el.removeClass("x-dlg-collapsed");
14557             this.resizeTo(this.el.getWidth(), this.restoreHeight);
14558         }
14559     },
14560
14561     /**
14562      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14563      * @return {Roo.TabPanel} The tabs component
14564      */
14565     initTabs : function(){
14566         var tabs = this.getTabs();
14567         while(tabs.getTab(0)){
14568             tabs.removeTab(0);
14569         }
14570         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14571             var dom = el.dom;
14572             tabs.addTab(Roo.id(dom), dom.title);
14573             dom.title = "";
14574         });
14575         tabs.activate(0);
14576         return tabs;
14577     },
14578
14579     // private
14580     beforeResize : function(){
14581         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14582     },
14583
14584     // private
14585     onResize : function(){
14586         this.refreshSize();
14587         this.syncBodyHeight();
14588         this.adjustAssets();
14589         this.focus();
14590         this.fireEvent("resize", this, this.size.width, this.size.height);
14591     },
14592
14593     // private
14594     onKeyDown : function(e){
14595         if(this.isVisible()){
14596             this.fireEvent("keydown", this, e);
14597         }
14598     },
14599
14600     /**
14601      * Resizes the dialog.
14602      * @param {Number} width
14603      * @param {Number} height
14604      * @return {Roo.BasicDialog} this
14605      */
14606     resizeTo : function(width, height){
14607         this.el.setSize(width, height);
14608         this.size = {width: width, height: height};
14609         this.syncBodyHeight();
14610         if(this.fixedcenter){
14611             this.center();
14612         }
14613         if(this.isVisible()){
14614             this.constrainXY();
14615             this.adjustAssets();
14616         }
14617         this.fireEvent("resize", this, width, height);
14618         return this;
14619     },
14620
14621
14622     /**
14623      * Resizes the dialog to fit the specified content size.
14624      * @param {Number} width
14625      * @param {Number} height
14626      * @return {Roo.BasicDialog} this
14627      */
14628     setContentSize : function(w, h){
14629         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14630         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14631         //if(!this.el.isBorderBox()){
14632             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14633             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14634         //}
14635         if(this.tabs){
14636             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14637             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14638         }
14639         this.resizeTo(w, h);
14640         return this;
14641     },
14642
14643     /**
14644      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
14645      * executed in response to a particular key being pressed while the dialog is active.
14646      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14647      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14648      * @param {Function} fn The function to call
14649      * @param {Object} scope (optional) The scope of the function
14650      * @return {Roo.BasicDialog} this
14651      */
14652     addKeyListener : function(key, fn, scope){
14653         var keyCode, shift, ctrl, alt;
14654         if(typeof key == "object" && !(key instanceof Array)){
14655             keyCode = key["key"];
14656             shift = key["shift"];
14657             ctrl = key["ctrl"];
14658             alt = key["alt"];
14659         }else{
14660             keyCode = key;
14661         }
14662         var handler = function(dlg, e){
14663             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14664                 var k = e.getKey();
14665                 if(keyCode instanceof Array){
14666                     for(var i = 0, len = keyCode.length; i < len; i++){
14667                         if(keyCode[i] == k){
14668                           fn.call(scope || window, dlg, k, e);
14669                           return;
14670                         }
14671                     }
14672                 }else{
14673                     if(k == keyCode){
14674                         fn.call(scope || window, dlg, k, e);
14675                     }
14676                 }
14677             }
14678         };
14679         this.on("keydown", handler);
14680         return this;
14681     },
14682
14683     /**
14684      * Returns the TabPanel component (creates it if it doesn't exist).
14685      * Note: If you wish to simply check for the existence of tabs without creating them,
14686      * check for a null 'tabs' property.
14687      * @return {Roo.TabPanel} The tabs component
14688      */
14689     getTabs : function(){
14690         if(!this.tabs){
14691             this.el.addClass("x-dlg-auto-tabs");
14692             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14693             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14694         }
14695         return this.tabs;
14696     },
14697
14698     /**
14699      * Adds a button to the footer section of the dialog.
14700      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14701      * object or a valid Roo.DomHelper element config
14702      * @param {Function} handler The function called when the button is clicked
14703      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14704      * @return {Roo.Button} The new button
14705      */
14706     addButton : function(config, handler, scope){
14707         var dh = Roo.DomHelper;
14708         if(!this.footer){
14709             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14710         }
14711         if(!this.btnContainer){
14712             var tb = this.footer.createChild({
14713
14714                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14715                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14716             }, null, true);
14717             this.btnContainer = tb.firstChild.firstChild.firstChild;
14718         }
14719         var bconfig = {
14720             handler: handler,
14721             scope: scope,
14722             minWidth: this.minButtonWidth,
14723             hideParent:true
14724         };
14725         if(typeof config == "string"){
14726             bconfig.text = config;
14727         }else{
14728             if(config.tag){
14729                 bconfig.dhconfig = config;
14730             }else{
14731                 Roo.apply(bconfig, config);
14732             }
14733         }
14734         var fc = false;
14735         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14736             bconfig.position = Math.max(0, bconfig.position);
14737             fc = this.btnContainer.childNodes[bconfig.position];
14738         }
14739          
14740         var btn = new Roo.Button(
14741             fc ? 
14742                 this.btnContainer.insertBefore(document.createElement("td"),fc)
14743                 : this.btnContainer.appendChild(document.createElement("td")),
14744             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
14745             bconfig
14746         );
14747         this.syncBodyHeight();
14748         if(!this.buttons){
14749             /**
14750              * Array of all the buttons that have been added to this dialog via addButton
14751              * @type Array
14752              */
14753             this.buttons = [];
14754         }
14755         this.buttons.push(btn);
14756         return btn;
14757     },
14758
14759     /**
14760      * Sets the default button to be focused when the dialog is displayed.
14761      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14762      * @return {Roo.BasicDialog} this
14763      */
14764     setDefaultButton : function(btn){
14765         this.defaultButton = btn;
14766         return this;
14767     },
14768
14769     // private
14770     getHeaderFooterHeight : function(safe){
14771         var height = 0;
14772         if(this.header){
14773            height += this.header.getHeight();
14774         }
14775         if(this.footer){
14776            var fm = this.footer.getMargins();
14777             height += (this.footer.getHeight()+fm.top+fm.bottom);
14778         }
14779         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14780         height += this.centerBg.getPadding("tb");
14781         return height;
14782     },
14783
14784     // private
14785     syncBodyHeight : function(){
14786         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
14787         var height = this.size.height - this.getHeaderFooterHeight(false);
14788         bd.setHeight(height-bd.getMargins("tb"));
14789         var hh = this.header.getHeight();
14790         var h = this.size.height-hh;
14791         cb.setHeight(h);
14792         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14793         bw.setHeight(h-cb.getPadding("tb"));
14794         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14795         bd.setWidth(bw.getWidth(true));
14796         if(this.tabs){
14797             this.tabs.syncHeight();
14798             if(Roo.isIE){
14799                 this.tabs.el.repaint();
14800             }
14801         }
14802     },
14803
14804     /**
14805      * Restores the previous state of the dialog if Roo.state is configured.
14806      * @return {Roo.BasicDialog} this
14807      */
14808     restoreState : function(){
14809         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14810         if(box && box.width){
14811             this.xy = [box.x, box.y];
14812             this.resizeTo(box.width, box.height);
14813         }
14814         return this;
14815     },
14816
14817     // private
14818     beforeShow : function(){
14819         this.expand();
14820         if(this.fixedcenter){
14821             this.xy = this.el.getCenterXY(true);
14822         }
14823         if(this.modal){
14824             Roo.get(document.body).addClass("x-body-masked");
14825             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14826             this.mask.show();
14827         }
14828         this.constrainXY();
14829     },
14830
14831     // private
14832     animShow : function(){
14833         var b = Roo.get(this.animateTarget).getBox();
14834         this.proxy.setSize(b.width, b.height);
14835         this.proxy.setLocation(b.x, b.y);
14836         this.proxy.show();
14837         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
14838                     true, .35, this.showEl.createDelegate(this));
14839     },
14840
14841     /**
14842      * Shows the dialog.
14843      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
14844      * @return {Roo.BasicDialog} this
14845      */
14846     show : function(animateTarget){
14847         if (this.fireEvent("beforeshow", this) === false){
14848             return;
14849         }
14850         if(this.syncHeightBeforeShow){
14851             this.syncBodyHeight();
14852         }else if(this.firstShow){
14853             this.firstShow = false;
14854             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
14855         }
14856         this.animateTarget = animateTarget || this.animateTarget;
14857         if(!this.el.isVisible()){
14858             this.beforeShow();
14859             if(this.animateTarget && Roo.get(this.animateTarget)){
14860                 this.animShow();
14861             }else{
14862                 this.showEl();
14863             }
14864         }
14865         return this;
14866     },
14867
14868     // private
14869     showEl : function(){
14870         this.proxy.hide();
14871         this.el.setXY(this.xy);
14872         this.el.show();
14873         this.adjustAssets(true);
14874         this.toFront();
14875         this.focus();
14876         // IE peekaboo bug - fix found by Dave Fenwick
14877         if(Roo.isIE){
14878             this.el.repaint();
14879         }
14880         this.fireEvent("show", this);
14881     },
14882
14883     /**
14884      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
14885      * dialog itself will receive focus.
14886      */
14887     focus : function(){
14888         if(this.defaultButton){
14889             this.defaultButton.focus();
14890         }else{
14891             this.focusEl.focus();
14892         }
14893     },
14894
14895     // private
14896     constrainXY : function(){
14897         if(this.constraintoviewport !== false){
14898             if(!this.viewSize){
14899                 if(this.container){
14900                     var s = this.container.getSize();
14901                     this.viewSize = [s.width, s.height];
14902                 }else{
14903                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
14904                 }
14905             }
14906             var s = Roo.get(this.container||document).getScroll();
14907
14908             var x = this.xy[0], y = this.xy[1];
14909             var w = this.size.width, h = this.size.height;
14910             var vw = this.viewSize[0], vh = this.viewSize[1];
14911             // only move it if it needs it
14912             var moved = false;
14913             // first validate right/bottom
14914             if(x + w > vw+s.left){
14915                 x = vw - w;
14916                 moved = true;
14917             }
14918             if(y + h > vh+s.top){
14919                 y = vh - h;
14920                 moved = true;
14921             }
14922             // then make sure top/left isn't negative
14923             if(x < s.left){
14924                 x = s.left;
14925                 moved = true;
14926             }
14927             if(y < s.top){
14928                 y = s.top;
14929                 moved = true;
14930             }
14931             if(moved){
14932                 // cache xy
14933                 this.xy = [x, y];
14934                 if(this.isVisible()){
14935                     this.el.setLocation(x, y);
14936                     this.adjustAssets();
14937                 }
14938             }
14939         }
14940     },
14941
14942     // private
14943     onDrag : function(){
14944         if(!this.proxyDrag){
14945             this.xy = this.el.getXY();
14946             this.adjustAssets();
14947         }
14948     },
14949
14950     // private
14951     adjustAssets : function(doShow){
14952         var x = this.xy[0], y = this.xy[1];
14953         var w = this.size.width, h = this.size.height;
14954         if(doShow === true){
14955             if(this.shadow){
14956                 this.shadow.show(this.el);
14957             }
14958             if(this.shim){
14959                 this.shim.show();
14960             }
14961         }
14962         if(this.shadow && this.shadow.isVisible()){
14963             this.shadow.show(this.el);
14964         }
14965         if(this.shim && this.shim.isVisible()){
14966             this.shim.setBounds(x, y, w, h);
14967         }
14968     },
14969
14970     // private
14971     adjustViewport : function(w, h){
14972         if(!w || !h){
14973             w = Roo.lib.Dom.getViewWidth();
14974             h = Roo.lib.Dom.getViewHeight();
14975         }
14976         // cache the size
14977         this.viewSize = [w, h];
14978         if(this.modal && this.mask.isVisible()){
14979             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
14980             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14981         }
14982         if(this.isVisible()){
14983             this.constrainXY();
14984         }
14985     },
14986
14987     /**
14988      * Destroys this dialog and all its supporting elements (including any tabs, shim,
14989      * shadow, proxy, mask, etc.)  Also removes all event listeners.
14990      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
14991      */
14992     destroy : function(removeEl){
14993         if(this.isVisible()){
14994             this.animateTarget = null;
14995             this.hide();
14996         }
14997         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
14998         if(this.tabs){
14999             this.tabs.destroy(removeEl);
15000         }
15001         Roo.destroy(
15002              this.shim,
15003              this.proxy,
15004              this.resizer,
15005              this.close,
15006              this.mask
15007         );
15008         if(this.dd){
15009             this.dd.unreg();
15010         }
15011         if(this.buttons){
15012            for(var i = 0, len = this.buttons.length; i < len; i++){
15013                this.buttons[i].destroy();
15014            }
15015         }
15016         this.el.removeAllListeners();
15017         if(removeEl === true){
15018             this.el.update("");
15019             this.el.remove();
15020         }
15021         Roo.DialogManager.unregister(this);
15022     },
15023
15024     // private
15025     startMove : function(){
15026         if(this.proxyDrag){
15027             this.proxy.show();
15028         }
15029         if(this.constraintoviewport !== false){
15030             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
15031         }
15032     },
15033
15034     // private
15035     endMove : function(){
15036         if(!this.proxyDrag){
15037             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
15038         }else{
15039             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
15040             this.proxy.hide();
15041         }
15042         this.refreshSize();
15043         this.adjustAssets();
15044         this.focus();
15045         this.fireEvent("move", this, this.xy[0], this.xy[1]);
15046     },
15047
15048     /**
15049      * Brings this dialog to the front of any other visible dialogs
15050      * @return {Roo.BasicDialog} this
15051      */
15052     toFront : function(){
15053         Roo.DialogManager.bringToFront(this);
15054         return this;
15055     },
15056
15057     /**
15058      * Sends this dialog to the back (under) of any other visible dialogs
15059      * @return {Roo.BasicDialog} this
15060      */
15061     toBack : function(){
15062         Roo.DialogManager.sendToBack(this);
15063         return this;
15064     },
15065
15066     /**
15067      * Centers this dialog in the viewport
15068      * @return {Roo.BasicDialog} this
15069      */
15070     center : function(){
15071         var xy = this.el.getCenterXY(true);
15072         this.moveTo(xy[0], xy[1]);
15073         return this;
15074     },
15075
15076     /**
15077      * Moves the dialog's top-left corner to the specified point
15078      * @param {Number} x
15079      * @param {Number} y
15080      * @return {Roo.BasicDialog} this
15081      */
15082     moveTo : function(x, y){
15083         this.xy = [x,y];
15084         if(this.isVisible()){
15085             this.el.setXY(this.xy);
15086             this.adjustAssets();
15087         }
15088         return this;
15089     },
15090
15091     /**
15092      * Aligns the dialog to the specified element
15093      * @param {String/HTMLElement/Roo.Element} element The element to align to.
15094      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
15095      * @param {Array} offsets (optional) Offset the positioning by [x, y]
15096      * @return {Roo.BasicDialog} this
15097      */
15098     alignTo : function(element, position, offsets){
15099         this.xy = this.el.getAlignToXY(element, position, offsets);
15100         if(this.isVisible()){
15101             this.el.setXY(this.xy);
15102             this.adjustAssets();
15103         }
15104         return this;
15105     },
15106
15107     /**
15108      * Anchors an element to another element and realigns it when the window is resized.
15109      * @param {String/HTMLElement/Roo.Element} element The element to align to.
15110      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
15111      * @param {Array} offsets (optional) Offset the positioning by [x, y]
15112      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
15113      * is a number, it is used as the buffer delay (defaults to 50ms).
15114      * @return {Roo.BasicDialog} this
15115      */
15116     anchorTo : function(el, alignment, offsets, monitorScroll){
15117         var action = function(){
15118             this.alignTo(el, alignment, offsets);
15119         };
15120         Roo.EventManager.onWindowResize(action, this);
15121         var tm = typeof monitorScroll;
15122         if(tm != 'undefined'){
15123             Roo.EventManager.on(window, 'scroll', action, this,
15124                 {buffer: tm == 'number' ? monitorScroll : 50});
15125         }
15126         action.call(this);
15127         return this;
15128     },
15129
15130     /**
15131      * Returns true if the dialog is visible
15132      * @return {Boolean}
15133      */
15134     isVisible : function(){
15135         return this.el.isVisible();
15136     },
15137
15138     // private
15139     animHide : function(callback){
15140         var b = Roo.get(this.animateTarget).getBox();
15141         this.proxy.show();
15142         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
15143         this.el.hide();
15144         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
15145                     this.hideEl.createDelegate(this, [callback]));
15146     },
15147
15148     /**
15149      * Hides the dialog.
15150      * @param {Function} callback (optional) Function to call when the dialog is hidden
15151      * @return {Roo.BasicDialog} this
15152      */
15153     hide : function(callback){
15154         if (this.fireEvent("beforehide", this) === false){
15155             return;
15156         }
15157         if(this.shadow){
15158             this.shadow.hide();
15159         }
15160         if(this.shim) {
15161           this.shim.hide();
15162         }
15163         // sometimes animateTarget seems to get set.. causing problems...
15164         // this just double checks..
15165         if(this.animateTarget && Roo.get(this.animateTarget)) {
15166            this.animHide(callback);
15167         }else{
15168             this.el.hide();
15169             this.hideEl(callback);
15170         }
15171         return this;
15172     },
15173
15174     // private
15175     hideEl : function(callback){
15176         this.proxy.hide();
15177         if(this.modal){
15178             this.mask.hide();
15179             Roo.get(document.body).removeClass("x-body-masked");
15180         }
15181         this.fireEvent("hide", this);
15182         if(typeof callback == "function"){
15183             callback();
15184         }
15185     },
15186
15187     // private
15188     hideAction : function(){
15189         this.setLeft("-10000px");
15190         this.setTop("-10000px");
15191         this.setStyle("visibility", "hidden");
15192     },
15193
15194     // private
15195     refreshSize : function(){
15196         this.size = this.el.getSize();
15197         this.xy = this.el.getXY();
15198         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
15199     },
15200
15201     // private
15202     // z-index is managed by the DialogManager and may be overwritten at any time
15203     setZIndex : function(index){
15204         if(this.modal){
15205             this.mask.setStyle("z-index", index);
15206         }
15207         if(this.shim){
15208             this.shim.setStyle("z-index", ++index);
15209         }
15210         if(this.shadow){
15211             this.shadow.setZIndex(++index);
15212         }
15213         this.el.setStyle("z-index", ++index);
15214         if(this.proxy){
15215             this.proxy.setStyle("z-index", ++index);
15216         }
15217         if(this.resizer){
15218             this.resizer.proxy.setStyle("z-index", ++index);
15219         }
15220
15221         this.lastZIndex = index;
15222     },
15223
15224     /**
15225      * Returns the element for this dialog
15226      * @return {Roo.Element} The underlying dialog Element
15227      */
15228     getEl : function(){
15229         return this.el;
15230     }
15231 });
15232
15233 /**
15234  * @class Roo.DialogManager
15235  * Provides global access to BasicDialogs that have been created and
15236  * support for z-indexing (layering) multiple open dialogs.
15237  */
15238 Roo.DialogManager = function(){
15239     var list = {};
15240     var accessList = [];
15241     var front = null;
15242
15243     // private
15244     var sortDialogs = function(d1, d2){
15245         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
15246     };
15247
15248     // private
15249     var orderDialogs = function(){
15250         accessList.sort(sortDialogs);
15251         var seed = Roo.DialogManager.zseed;
15252         for(var i = 0, len = accessList.length; i < len; i++){
15253             var dlg = accessList[i];
15254             if(dlg){
15255                 dlg.setZIndex(seed + (i*10));
15256             }
15257         }
15258     };
15259
15260     return {
15261         /**
15262          * The starting z-index for BasicDialogs (defaults to 9000)
15263          * @type Number The z-index value
15264          */
15265         zseed : 9000,
15266
15267         // private
15268         register : function(dlg){
15269             list[dlg.id] = dlg;
15270             accessList.push(dlg);
15271         },
15272
15273         // private
15274         unregister : function(dlg){
15275             delete list[dlg.id];
15276             var i=0;
15277             var len=0;
15278             if(!accessList.indexOf){
15279                 for(  i = 0, len = accessList.length; i < len; i++){
15280                     if(accessList[i] == dlg){
15281                         accessList.splice(i, 1);
15282                         return;
15283                     }
15284                 }
15285             }else{
15286                  i = accessList.indexOf(dlg);
15287                 if(i != -1){
15288                     accessList.splice(i, 1);
15289                 }
15290             }
15291         },
15292
15293         /**
15294          * Gets a registered dialog by id
15295          * @param {String/Object} id The id of the dialog or a dialog
15296          * @return {Roo.BasicDialog} this
15297          */
15298         get : function(id){
15299             return typeof id == "object" ? id : list[id];
15300         },
15301
15302         /**
15303          * Brings the specified dialog to the front
15304          * @param {String/Object} dlg The id of the dialog or a dialog
15305          * @return {Roo.BasicDialog} this
15306          */
15307         bringToFront : function(dlg){
15308             dlg = this.get(dlg);
15309             if(dlg != front){
15310                 front = dlg;
15311                 dlg._lastAccess = new Date().getTime();
15312                 orderDialogs();
15313             }
15314             return dlg;
15315         },
15316
15317         /**
15318          * Sends the specified dialog to the back
15319          * @param {String/Object} dlg The id of the dialog or a dialog
15320          * @return {Roo.BasicDialog} this
15321          */
15322         sendToBack : function(dlg){
15323             dlg = this.get(dlg);
15324             dlg._lastAccess = -(new Date().getTime());
15325             orderDialogs();
15326             return dlg;
15327         },
15328
15329         /**
15330          * Hides all dialogs
15331          */
15332         hideAll : function(){
15333             for(var id in list){
15334                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
15335                     list[id].hide();
15336                 }
15337             }
15338         }
15339     };
15340 }();
15341
15342 /**
15343  * @class Roo.LayoutDialog
15344  * @extends Roo.BasicDialog
15345  * Dialog which provides adjustments for working with a layout in a Dialog.
15346  * Add your necessary layout config options to the dialog's config.<br>
15347  * Example usage (including a nested layout):
15348  * <pre><code>
15349 if(!dialog){
15350     dialog = new Roo.LayoutDialog("download-dlg", {
15351         modal: true,
15352         width:600,
15353         height:450,
15354         shadow:true,
15355         minWidth:500,
15356         minHeight:350,
15357         autoTabs:true,
15358         proxyDrag:true,
15359         // layout config merges with the dialog config
15360         center:{
15361             tabPosition: "top",
15362             alwaysShowTabs: true
15363         }
15364     });
15365     dialog.addKeyListener(27, dialog.hide, dialog);
15366     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
15367     dialog.addButton("Build It!", this.getDownload, this);
15368
15369     // we can even add nested layouts
15370     var innerLayout = new Roo.BorderLayout("dl-inner", {
15371         east: {
15372             initialSize: 200,
15373             autoScroll:true,
15374             split:true
15375         },
15376         center: {
15377             autoScroll:true
15378         }
15379     });
15380     innerLayout.beginUpdate();
15381     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
15382     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
15383     innerLayout.endUpdate(true);
15384
15385     var layout = dialog.getLayout();
15386     layout.beginUpdate();
15387     layout.add("center", new Roo.ContentPanel("standard-panel",
15388                         {title: "Download the Source", fitToFrame:true}));
15389     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
15390                {title: "Build your own roo.js"}));
15391     layout.getRegion("center").showPanel(sp);
15392     layout.endUpdate();
15393 }
15394 </code></pre>
15395     * @constructor
15396     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
15397     * @param {Object} config configuration options
15398   */
15399 Roo.LayoutDialog = function(el, cfg){
15400     
15401     var config=  cfg;
15402     if (typeof(cfg) == 'undefined') {
15403         config = Roo.apply({}, el);
15404         // not sure why we use documentElement here.. - it should always be body.
15405         // IE7 borks horribly if we use documentElement.
15406         // webkit also does not like documentElement - it creates a body element...
15407         el = Roo.get( document.body || document.documentElement ).createChild();
15408         //config.autoCreate = true;
15409     }
15410     
15411     
15412     config.autoTabs = false;
15413     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
15414     this.body.setStyle({overflow:"hidden", position:"relative"});
15415     this.layout = new Roo.BorderLayout(this.body.dom, config);
15416     this.layout.monitorWindowResize = false;
15417     this.el.addClass("x-dlg-auto-layout");
15418     // fix case when center region overwrites center function
15419     this.center = Roo.BasicDialog.prototype.center;
15420     this.on("show", this.layout.layout, this.layout, true);
15421     if (config.items) {
15422         var xitems = config.items;
15423         delete config.items;
15424         Roo.each(xitems, this.addxtype, this);
15425     }
15426     
15427     
15428 };
15429 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
15430     /**
15431      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
15432      * @deprecated
15433      */
15434     endUpdate : function(){
15435         this.layout.endUpdate();
15436     },
15437
15438     /**
15439      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
15440      *  @deprecated
15441      */
15442     beginUpdate : function(){
15443         this.layout.beginUpdate();
15444     },
15445
15446     /**
15447      * Get the BorderLayout for this dialog
15448      * @return {Roo.BorderLayout}
15449      */
15450     getLayout : function(){
15451         return this.layout;
15452     },
15453
15454     showEl : function(){
15455         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
15456         if(Roo.isIE7){
15457             this.layout.layout();
15458         }
15459     },
15460
15461     // private
15462     // Use the syncHeightBeforeShow config option to control this automatically
15463     syncBodyHeight : function(){
15464         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
15465         if(this.layout){this.layout.layout();}
15466     },
15467     
15468       /**
15469      * Add an xtype element (actually adds to the layout.)
15470      * @return {Object} xdata xtype object data.
15471      */
15472     
15473     addxtype : function(c) {
15474         return this.layout.addxtype(c);
15475     }
15476 });/*
15477  * Based on:
15478  * Ext JS Library 1.1.1
15479  * Copyright(c) 2006-2007, Ext JS, LLC.
15480  *
15481  * Originally Released Under LGPL - original licence link has changed is not relivant.
15482  *
15483  * Fork - LGPL
15484  * <script type="text/javascript">
15485  */
15486  
15487 /**
15488  * @class Roo.MessageBox
15489  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
15490  * Example usage:
15491  *<pre><code>
15492 // Basic alert:
15493 Roo.Msg.alert('Status', 'Changes saved successfully.');
15494
15495 // Prompt for user data:
15496 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
15497     if (btn == 'ok'){
15498         // process text value...
15499     }
15500 });
15501
15502 // Show a dialog using config options:
15503 Roo.Msg.show({
15504    title:'Save Changes?',
15505    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
15506    buttons: Roo.Msg.YESNOCANCEL,
15507    fn: processResult,
15508    animEl: 'elId'
15509 });
15510 </code></pre>
15511  * @singleton
15512  */
15513 Roo.MessageBox = function(){
15514     var dlg, opt, mask, waitTimer;
15515     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
15516     var buttons, activeTextEl, bwidth;
15517
15518     // private
15519     var handleButton = function(button){
15520         dlg.hide();
15521         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
15522     };
15523
15524     // private
15525     var handleHide = function(){
15526         if(opt && opt.cls){
15527             dlg.el.removeClass(opt.cls);
15528         }
15529         if(waitTimer){
15530             Roo.TaskMgr.stop(waitTimer);
15531             waitTimer = null;
15532         }
15533     };
15534
15535     // private
15536     var updateButtons = function(b){
15537         var width = 0;
15538         if(!b){
15539             buttons["ok"].hide();
15540             buttons["cancel"].hide();
15541             buttons["yes"].hide();
15542             buttons["no"].hide();
15543             dlg.footer.dom.style.display = 'none';
15544             return width;
15545         }
15546         dlg.footer.dom.style.display = '';
15547         for(var k in buttons){
15548             if(typeof buttons[k] != "function"){
15549                 if(b[k]){
15550                     buttons[k].show();
15551                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15552                     width += buttons[k].el.getWidth()+15;
15553                 }else{
15554                     buttons[k].hide();
15555                 }
15556             }
15557         }
15558         return width;
15559     };
15560
15561     // private
15562     var handleEsc = function(d, k, e){
15563         if(opt && opt.closable !== false){
15564             dlg.hide();
15565         }
15566         if(e){
15567             e.stopEvent();
15568         }
15569     };
15570
15571     return {
15572         /**
15573          * Returns a reference to the underlying {@link Roo.BasicDialog} element
15574          * @return {Roo.BasicDialog} The BasicDialog element
15575          */
15576         getDialog : function(){
15577            if(!dlg){
15578                 dlg = new Roo.BasicDialog("x-msg-box", {
15579                     autoCreate : true,
15580                     shadow: true,
15581                     draggable: true,
15582                     resizable:false,
15583                     constraintoviewport:false,
15584                     fixedcenter:true,
15585                     collapsible : false,
15586                     shim:true,
15587                     modal: true,
15588                     width:400, height:100,
15589                     buttonAlign:"center",
15590                     closeClick : function(){
15591                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15592                             handleButton("no");
15593                         }else{
15594                             handleButton("cancel");
15595                         }
15596                     }
15597                 });
15598                 dlg.on("hide", handleHide);
15599                 mask = dlg.mask;
15600                 dlg.addKeyListener(27, handleEsc);
15601                 buttons = {};
15602                 var bt = this.buttonText;
15603                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15604                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15605                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15606                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15607                 bodyEl = dlg.body.createChild({
15608
15609                     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>'
15610                 });
15611                 msgEl = bodyEl.dom.firstChild;
15612                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15613                 textboxEl.enableDisplayMode();
15614                 textboxEl.addKeyListener([10,13], function(){
15615                     if(dlg.isVisible() && opt && opt.buttons){
15616                         if(opt.buttons.ok){
15617                             handleButton("ok");
15618                         }else if(opt.buttons.yes){
15619                             handleButton("yes");
15620                         }
15621                     }
15622                 });
15623                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15624                 textareaEl.enableDisplayMode();
15625                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15626                 progressEl.enableDisplayMode();
15627                 var pf = progressEl.dom.firstChild;
15628                 if (pf) {
15629                     pp = Roo.get(pf.firstChild);
15630                     pp.setHeight(pf.offsetHeight);
15631                 }
15632                 
15633             }
15634             return dlg;
15635         },
15636
15637         /**
15638          * Updates the message box body text
15639          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15640          * the XHTML-compliant non-breaking space character '&amp;#160;')
15641          * @return {Roo.MessageBox} This message box
15642          */
15643         updateText : function(text){
15644             if(!dlg.isVisible() && !opt.width){
15645                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15646             }
15647             msgEl.innerHTML = text || '&#160;';
15648       
15649             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
15650             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
15651             var w = Math.max(
15652                     Math.min(opt.width || cw , this.maxWidth), 
15653                     Math.max(opt.minWidth || this.minWidth, bwidth)
15654             );
15655             if(opt.prompt){
15656                 activeTextEl.setWidth(w);
15657             }
15658             if(dlg.isVisible()){
15659                 dlg.fixedcenter = false;
15660             }
15661             // to big, make it scoll.
15662             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
15663                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
15664                 bodyEl.dom.style.overflowY = 'auto !important';
15665             } else {
15666                 bodyEl.dom.style.height = '';
15667                 bodyEl.dom.style.overflowY = '';
15668             }
15669             if (cw > w) {
15670                 bodyEl.dom.style.overflowX = 'auto !important';
15671             } else {
15672                 bodyEl.dom.style.overflowX = '';
15673             }
15674             
15675             dlg.setContentSize(w, bodyEl.getHeight());
15676             if(dlg.isVisible()){
15677                 dlg.fixedcenter = true;
15678             }
15679             return this;
15680         },
15681
15682         /**
15683          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
15684          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15685          * @param {Number} value Any number between 0 and 1 (e.g., .5)
15686          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15687          * @return {Roo.MessageBox} This message box
15688          */
15689         updateProgress : function(value, text){
15690             if(text){
15691                 this.updateText(text);
15692             }
15693             if (pp) { // weird bug on my firefox - for some reason this is not defined
15694                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15695             }
15696             return this;
15697         },        
15698
15699         /**
15700          * Returns true if the message box is currently displayed
15701          * @return {Boolean} True if the message box is visible, else false
15702          */
15703         isVisible : function(){
15704             return dlg && dlg.isVisible();  
15705         },
15706
15707         /**
15708          * Hides the message box if it is displayed
15709          */
15710         hide : function(){
15711             if(this.isVisible()){
15712                 dlg.hide();
15713             }  
15714         },
15715
15716         /**
15717          * Displays a new message box, or reinitializes an existing message box, based on the config options
15718          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15719          * The following config object properties are supported:
15720          * <pre>
15721 Property    Type             Description
15722 ----------  ---------------  ------------------------------------------------------------------------------------
15723 animEl            String/Element   An id or Element from which the message box should animate as it opens and
15724                                    closes (defaults to undefined)
15725 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15726                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
15727 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
15728                                    progress and wait dialogs will ignore this property and always hide the
15729                                    close button as they can only be closed programmatically.
15730 cls               String           A custom CSS class to apply to the message box element
15731 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
15732                                    displayed (defaults to 75)
15733 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
15734                                    function will be btn (the name of the button that was clicked, if applicable,
15735                                    e.g. "ok"), and text (the value of the active text field, if applicable).
15736                                    Progress and wait dialogs will ignore this option since they do not respond to
15737                                    user actions and can only be closed programmatically, so any required function
15738                                    should be called by the same code after it closes the dialog.
15739 icon              String           A CSS class that provides a background image to be used as an icon for
15740                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15741 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
15742 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
15743 modal             Boolean          False to allow user interaction with the page while the message box is
15744                                    displayed (defaults to true)
15745 msg               String           A string that will replace the existing message box body text (defaults
15746                                    to the XHTML-compliant non-breaking space character '&#160;')
15747 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
15748 progress          Boolean          True to display a progress bar (defaults to false)
15749 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
15750 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
15751 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
15752 title             String           The title text
15753 value             String           The string value to set into the active textbox element if displayed
15754 wait              Boolean          True to display a progress bar (defaults to false)
15755 width             Number           The width of the dialog in pixels
15756 </pre>
15757          *
15758          * Example usage:
15759          * <pre><code>
15760 Roo.Msg.show({
15761    title: 'Address',
15762    msg: 'Please enter your address:',
15763    width: 300,
15764    buttons: Roo.MessageBox.OKCANCEL,
15765    multiline: true,
15766    fn: saveAddress,
15767    animEl: 'addAddressBtn'
15768 });
15769 </code></pre>
15770          * @param {Object} config Configuration options
15771          * @return {Roo.MessageBox} This message box
15772          */
15773         show : function(options)
15774         {
15775             
15776             // this causes nightmares if you show one dialog after another
15777             // especially on callbacks..
15778              
15779             if(this.isVisible()){
15780                 
15781                 this.hide();
15782                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML )
15783                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
15784                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
15785                 
15786             }
15787             var d = this.getDialog();
15788             opt = options;
15789             d.setTitle(opt.title || "&#160;");
15790             d.close.setDisplayed(opt.closable !== false);
15791             activeTextEl = textboxEl;
15792             opt.prompt = opt.prompt || (opt.multiline ? true : false);
15793             if(opt.prompt){
15794                 if(opt.multiline){
15795                     textboxEl.hide();
15796                     textareaEl.show();
15797                     textareaEl.setHeight(typeof opt.multiline == "number" ?
15798                         opt.multiline : this.defaultTextHeight);
15799                     activeTextEl = textareaEl;
15800                 }else{
15801                     textboxEl.show();
15802                     textareaEl.hide();
15803                 }
15804             }else{
15805                 textboxEl.hide();
15806                 textareaEl.hide();
15807             }
15808             progressEl.setDisplayed(opt.progress === true);
15809             this.updateProgress(0);
15810             activeTextEl.dom.value = opt.value || "";
15811             if(opt.prompt){
15812                 dlg.setDefaultButton(activeTextEl);
15813             }else{
15814                 var bs = opt.buttons;
15815                 var db = null;
15816                 if(bs && bs.ok){
15817                     db = buttons["ok"];
15818                 }else if(bs && bs.yes){
15819                     db = buttons["yes"];
15820                 }
15821                 dlg.setDefaultButton(db);
15822             }
15823             bwidth = updateButtons(opt.buttons);
15824             this.updateText(opt.msg);
15825             if(opt.cls){
15826                 d.el.addClass(opt.cls);
15827             }
15828             d.proxyDrag = opt.proxyDrag === true;
15829             d.modal = opt.modal !== false;
15830             d.mask = opt.modal !== false ? mask : false;
15831             if(!d.isVisible()){
15832                 // force it to the end of the z-index stack so it gets a cursor in FF
15833                 document.body.appendChild(dlg.el.dom);
15834                 d.animateTarget = null;
15835                 d.show(options.animEl);
15836             }
15837             return this;
15838         },
15839
15840         /**
15841          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
15842          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
15843          * and closing the message box when the process is complete.
15844          * @param {String} title The title bar text
15845          * @param {String} msg The message box body text
15846          * @return {Roo.MessageBox} This message box
15847          */
15848         progress : function(title, msg){
15849             this.show({
15850                 title : title,
15851                 msg : msg,
15852                 buttons: false,
15853                 progress:true,
15854                 closable:false,
15855                 minWidth: this.minProgressWidth,
15856                 modal : true
15857             });
15858             return this;
15859         },
15860
15861         /**
15862          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
15863          * If a callback function is passed it will be called after the user clicks the button, and the
15864          * id of the button that was clicked will be passed as the only parameter to the callback
15865          * (could also be the top-right close button).
15866          * @param {String} title The title bar text
15867          * @param {String} msg The message box body text
15868          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15869          * @param {Object} scope (optional) The scope of the callback function
15870          * @return {Roo.MessageBox} This message box
15871          */
15872         alert : function(title, msg, fn, scope){
15873             this.show({
15874                 title : title,
15875                 msg : msg,
15876                 buttons: this.OK,
15877                 fn: fn,
15878                 scope : scope,
15879                 modal : true
15880             });
15881             return this;
15882         },
15883
15884         /**
15885          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
15886          * interaction while waiting for a long-running process to complete that does not have defined intervals.
15887          * You are responsible for closing the message box when the process is complete.
15888          * @param {String} msg The message box body text
15889          * @param {String} title (optional) The title bar text
15890          * @return {Roo.MessageBox} This message box
15891          */
15892         wait : function(msg, title){
15893             this.show({
15894                 title : title,
15895                 msg : msg,
15896                 buttons: false,
15897                 closable:false,
15898                 progress:true,
15899                 modal:true,
15900                 width:300,
15901                 wait:true
15902             });
15903             waitTimer = Roo.TaskMgr.start({
15904                 run: function(i){
15905                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
15906                 },
15907                 interval: 1000
15908             });
15909             return this;
15910         },
15911
15912         /**
15913          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
15914          * If a callback function is passed it will be called after the user clicks either button, and the id of the
15915          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
15916          * @param {String} title The title bar text
15917          * @param {String} msg The message box body text
15918          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15919          * @param {Object} scope (optional) The scope of the callback function
15920          * @return {Roo.MessageBox} This message box
15921          */
15922         confirm : function(title, msg, fn, scope){
15923             this.show({
15924                 title : title,
15925                 msg : msg,
15926                 buttons: this.YESNO,
15927                 fn: fn,
15928                 scope : scope,
15929                 modal : true
15930             });
15931             return this;
15932         },
15933
15934         /**
15935          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
15936          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
15937          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
15938          * (could also be the top-right close button) and the text that was entered will be passed as the two
15939          * parameters to the callback.
15940          * @param {String} title The title bar text
15941          * @param {String} msg The message box body text
15942          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15943          * @param {Object} scope (optional) The scope of the callback function
15944          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
15945          * property, or the height in pixels to create the textbox (defaults to false / single-line)
15946          * @return {Roo.MessageBox} This message box
15947          */
15948         prompt : function(title, msg, fn, scope, multiline){
15949             this.show({
15950                 title : title,
15951                 msg : msg,
15952                 buttons: this.OKCANCEL,
15953                 fn: fn,
15954                 minWidth:250,
15955                 scope : scope,
15956                 prompt:true,
15957                 multiline: multiline,
15958                 modal : true
15959             });
15960             return this;
15961         },
15962
15963         /**
15964          * Button config that displays a single OK button
15965          * @type Object
15966          */
15967         OK : {ok:true},
15968         /**
15969          * Button config that displays Yes and No buttons
15970          * @type Object
15971          */
15972         YESNO : {yes:true, no:true},
15973         /**
15974          * Button config that displays OK and Cancel buttons
15975          * @type Object
15976          */
15977         OKCANCEL : {ok:true, cancel:true},
15978         /**
15979          * Button config that displays Yes, No and Cancel buttons
15980          * @type Object
15981          */
15982         YESNOCANCEL : {yes:true, no:true, cancel:true},
15983
15984         /**
15985          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
15986          * @type Number
15987          */
15988         defaultTextHeight : 75,
15989         /**
15990          * The maximum width in pixels of the message box (defaults to 600)
15991          * @type Number
15992          */
15993         maxWidth : 600,
15994         /**
15995          * The minimum width in pixels of the message box (defaults to 100)
15996          * @type Number
15997          */
15998         minWidth : 100,
15999         /**
16000          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
16001          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
16002          * @type Number
16003          */
16004         minProgressWidth : 250,
16005         /**
16006          * An object containing the default button text strings that can be overriden for localized language support.
16007          * Supported properties are: ok, cancel, yes and no.
16008          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
16009          * @type Object
16010          */
16011         buttonText : {
16012             ok : "OK",
16013             cancel : "Cancel",
16014             yes : "Yes",
16015             no : "No"
16016         }
16017     };
16018 }();
16019
16020 /**
16021  * Shorthand for {@link Roo.MessageBox}
16022  */
16023 Roo.Msg = Roo.MessageBox;/*
16024  * Based on:
16025  * Ext JS Library 1.1.1
16026  * Copyright(c) 2006-2007, Ext JS, LLC.
16027  *
16028  * Originally Released Under LGPL - original licence link has changed is not relivant.
16029  *
16030  * Fork - LGPL
16031  * <script type="text/javascript">
16032  */
16033 /**
16034  * @class Roo.QuickTips
16035  * Provides attractive and customizable tooltips for any element.
16036  * @singleton
16037  */
16038 Roo.QuickTips = function(){
16039     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
16040     var ce, bd, xy, dd;
16041     var visible = false, disabled = true, inited = false;
16042     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
16043     
16044     var onOver = function(e){
16045         if(disabled){
16046             return;
16047         }
16048         var t = e.getTarget();
16049         if(!t || t.nodeType !== 1 || t == document || t == document.body){
16050             return;
16051         }
16052         if(ce && t == ce.el){
16053             clearTimeout(hideProc);
16054             return;
16055         }
16056         if(t && tagEls[t.id]){
16057             tagEls[t.id].el = t;
16058             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
16059             return;
16060         }
16061         var ttp, et = Roo.fly(t);
16062         var ns = cfg.namespace;
16063         if(tm.interceptTitles && t.title){
16064             ttp = t.title;
16065             t.qtip = ttp;
16066             t.removeAttribute("title");
16067             e.preventDefault();
16068         }else{
16069             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
16070         }
16071         if(ttp){
16072             showProc = show.defer(tm.showDelay, tm, [{
16073                 el: t, 
16074                 text: ttp, 
16075                 width: et.getAttributeNS(ns, cfg.width),
16076                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
16077                 title: et.getAttributeNS(ns, cfg.title),
16078                     cls: et.getAttributeNS(ns, cfg.cls)
16079             }]);
16080         }
16081     };
16082     
16083     var onOut = function(e){
16084         clearTimeout(showProc);
16085         var t = e.getTarget();
16086         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
16087             hideProc = setTimeout(hide, tm.hideDelay);
16088         }
16089     };
16090     
16091     var onMove = function(e){
16092         if(disabled){
16093             return;
16094         }
16095         xy = e.getXY();
16096         xy[1] += 18;
16097         if(tm.trackMouse && ce){
16098             el.setXY(xy);
16099         }
16100     };
16101     
16102     var onDown = function(e){
16103         clearTimeout(showProc);
16104         clearTimeout(hideProc);
16105         if(!e.within(el)){
16106             if(tm.hideOnClick){
16107                 hide();
16108                 tm.disable();
16109                 tm.enable.defer(100, tm);
16110             }
16111         }
16112     };
16113     
16114     var getPad = function(){
16115         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
16116     };
16117
16118     var show = function(o){
16119         if(disabled){
16120             return;
16121         }
16122         clearTimeout(dismissProc);
16123         ce = o;
16124         if(removeCls){ // in case manually hidden
16125             el.removeClass(removeCls);
16126             removeCls = null;
16127         }
16128         if(ce.cls){
16129             el.addClass(ce.cls);
16130             removeCls = ce.cls;
16131         }
16132         if(ce.title){
16133             tipTitle.update(ce.title);
16134             tipTitle.show();
16135         }else{
16136             tipTitle.update('');
16137             tipTitle.hide();
16138         }
16139         el.dom.style.width  = tm.maxWidth+'px';
16140         //tipBody.dom.style.width = '';
16141         tipBodyText.update(o.text);
16142         var p = getPad(), w = ce.width;
16143         if(!w){
16144             var td = tipBodyText.dom;
16145             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
16146             if(aw > tm.maxWidth){
16147                 w = tm.maxWidth;
16148             }else if(aw < tm.minWidth){
16149                 w = tm.minWidth;
16150             }else{
16151                 w = aw;
16152             }
16153         }
16154         //tipBody.setWidth(w);
16155         el.setWidth(parseInt(w, 10) + p);
16156         if(ce.autoHide === false){
16157             close.setDisplayed(true);
16158             if(dd){
16159                 dd.unlock();
16160             }
16161         }else{
16162             close.setDisplayed(false);
16163             if(dd){
16164                 dd.lock();
16165             }
16166         }
16167         if(xy){
16168             el.avoidY = xy[1]-18;
16169             el.setXY(xy);
16170         }
16171         if(tm.animate){
16172             el.setOpacity(.1);
16173             el.setStyle("visibility", "visible");
16174             el.fadeIn({callback: afterShow});
16175         }else{
16176             afterShow();
16177         }
16178     };
16179     
16180     var afterShow = function(){
16181         if(ce){
16182             el.show();
16183             esc.enable();
16184             if(tm.autoDismiss && ce.autoHide !== false){
16185                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
16186             }
16187         }
16188     };
16189     
16190     var hide = function(noanim){
16191         clearTimeout(dismissProc);
16192         clearTimeout(hideProc);
16193         ce = null;
16194         if(el.isVisible()){
16195             esc.disable();
16196             if(noanim !== true && tm.animate){
16197                 el.fadeOut({callback: afterHide});
16198             }else{
16199                 afterHide();
16200             } 
16201         }
16202     };
16203     
16204     var afterHide = function(){
16205         el.hide();
16206         if(removeCls){
16207             el.removeClass(removeCls);
16208             removeCls = null;
16209         }
16210     };
16211     
16212     return {
16213         /**
16214         * @cfg {Number} minWidth
16215         * The minimum width of the quick tip (defaults to 40)
16216         */
16217        minWidth : 40,
16218         /**
16219         * @cfg {Number} maxWidth
16220         * The maximum width of the quick tip (defaults to 300)
16221         */
16222        maxWidth : 300,
16223         /**
16224         * @cfg {Boolean} interceptTitles
16225         * True to automatically use the element's DOM title value if available (defaults to false)
16226         */
16227        interceptTitles : false,
16228         /**
16229         * @cfg {Boolean} trackMouse
16230         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
16231         */
16232        trackMouse : false,
16233         /**
16234         * @cfg {Boolean} hideOnClick
16235         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
16236         */
16237        hideOnClick : true,
16238         /**
16239         * @cfg {Number} showDelay
16240         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
16241         */
16242        showDelay : 500,
16243         /**
16244         * @cfg {Number} hideDelay
16245         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
16246         */
16247        hideDelay : 200,
16248         /**
16249         * @cfg {Boolean} autoHide
16250         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
16251         * Used in conjunction with hideDelay.
16252         */
16253        autoHide : true,
16254         /**
16255         * @cfg {Boolean}
16256         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
16257         * (defaults to true).  Used in conjunction with autoDismissDelay.
16258         */
16259        autoDismiss : true,
16260         /**
16261         * @cfg {Number}
16262         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
16263         */
16264        autoDismissDelay : 5000,
16265        /**
16266         * @cfg {Boolean} animate
16267         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
16268         */
16269        animate : false,
16270
16271        /**
16272         * @cfg {String} title
16273         * Title text to display (defaults to '').  This can be any valid HTML markup.
16274         */
16275         title: '',
16276        /**
16277         * @cfg {String} text
16278         * Body text to display (defaults to '').  This can be any valid HTML markup.
16279         */
16280         text : '',
16281        /**
16282         * @cfg {String} cls
16283         * A CSS class to apply to the base quick tip element (defaults to '').
16284         */
16285         cls : '',
16286        /**
16287         * @cfg {Number} width
16288         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
16289         * minWidth or maxWidth.
16290         */
16291         width : null,
16292
16293     /**
16294      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
16295      * or display QuickTips in a page.
16296      */
16297        init : function(){
16298           tm = Roo.QuickTips;
16299           cfg = tm.tagConfig;
16300           if(!inited){
16301               if(!Roo.isReady){ // allow calling of init() before onReady
16302                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
16303                   return;
16304               }
16305               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
16306               el.fxDefaults = {stopFx: true};
16307               // maximum custom styling
16308               //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>');
16309               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>');              
16310               tipTitle = el.child('h3');
16311               tipTitle.enableDisplayMode("block");
16312               tipBody = el.child('div.x-tip-bd');
16313               tipBodyText = el.child('div.x-tip-bd-inner');
16314               //bdLeft = el.child('div.x-tip-bd-left');
16315               //bdRight = el.child('div.x-tip-bd-right');
16316               close = el.child('div.x-tip-close');
16317               close.enableDisplayMode("block");
16318               close.on("click", hide);
16319               var d = Roo.get(document);
16320               d.on("mousedown", onDown);
16321               d.on("mouseover", onOver);
16322               d.on("mouseout", onOut);
16323               d.on("mousemove", onMove);
16324               esc = d.addKeyListener(27, hide);
16325               esc.disable();
16326               if(Roo.dd.DD){
16327                   dd = el.initDD("default", null, {
16328                       onDrag : function(){
16329                           el.sync();  
16330                       }
16331                   });
16332                   dd.setHandleElId(tipTitle.id);
16333                   dd.lock();
16334               }
16335               inited = true;
16336           }
16337           this.enable(); 
16338        },
16339
16340     /**
16341      * Configures a new quick tip instance and assigns it to a target element.  The following config options
16342      * are supported:
16343      * <pre>
16344 Property    Type                   Description
16345 ----------  ---------------------  ------------------------------------------------------------------------
16346 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
16347      * </ul>
16348      * @param {Object} config The config object
16349      */
16350        register : function(config){
16351            var cs = config instanceof Array ? config : arguments;
16352            for(var i = 0, len = cs.length; i < len; i++) {
16353                var c = cs[i];
16354                var target = c.target;
16355                if(target){
16356                    if(target instanceof Array){
16357                        for(var j = 0, jlen = target.length; j < jlen; j++){
16358                            tagEls[target[j]] = c;
16359                        }
16360                    }else{
16361                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
16362                    }
16363                }
16364            }
16365        },
16366
16367     /**
16368      * Removes this quick tip from its element and destroys it.
16369      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
16370      */
16371        unregister : function(el){
16372            delete tagEls[Roo.id(el)];
16373        },
16374
16375     /**
16376      * Enable this quick tip.
16377      */
16378        enable : function(){
16379            if(inited && disabled){
16380                locks.pop();
16381                if(locks.length < 1){
16382                    disabled = false;
16383                }
16384            }
16385        },
16386
16387     /**
16388      * Disable this quick tip.
16389      */
16390        disable : function(){
16391           disabled = true;
16392           clearTimeout(showProc);
16393           clearTimeout(hideProc);
16394           clearTimeout(dismissProc);
16395           if(ce){
16396               hide(true);
16397           }
16398           locks.push(1);
16399        },
16400
16401     /**
16402      * Returns true if the quick tip is enabled, else false.
16403      */
16404        isEnabled : function(){
16405             return !disabled;
16406        },
16407
16408         // private
16409        tagConfig : {
16410            namespace : "ext",
16411            attribute : "qtip",
16412            width : "width",
16413            target : "target",
16414            title : "qtitle",
16415            hide : "hide",
16416            cls : "qclass"
16417        }
16418    };
16419 }();
16420
16421 // backwards compat
16422 Roo.QuickTips.tips = Roo.QuickTips.register;/*
16423  * Based on:
16424  * Ext JS Library 1.1.1
16425  * Copyright(c) 2006-2007, Ext JS, LLC.
16426  *
16427  * Originally Released Under LGPL - original licence link has changed is not relivant.
16428  *
16429  * Fork - LGPL
16430  * <script type="text/javascript">
16431  */
16432  
16433
16434 /**
16435  * @class Roo.tree.TreePanel
16436  * @extends Roo.data.Tree
16437
16438  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
16439  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
16440  * @cfg {Boolean} enableDD true to enable drag and drop
16441  * @cfg {Boolean} enableDrag true to enable just drag
16442  * @cfg {Boolean} enableDrop true to enable just drop
16443  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
16444  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
16445  * @cfg {String} ddGroup The DD group this TreePanel belongs to
16446  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
16447  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
16448  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
16449  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
16450  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
16451  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
16452  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
16453  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
16454  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
16455  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
16456  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
16457  * @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>
16458  * @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>
16459  * 
16460  * @constructor
16461  * @param {String/HTMLElement/Element} el The container element
16462  * @param {Object} config
16463  */
16464 Roo.tree.TreePanel = function(el, config){
16465     var root = false;
16466     var loader = false;
16467     if (config.root) {
16468         root = config.root;
16469         delete config.root;
16470     }
16471     if (config.loader) {
16472         loader = config.loader;
16473         delete config.loader;
16474     }
16475     
16476     Roo.apply(this, config);
16477     Roo.tree.TreePanel.superclass.constructor.call(this);
16478     this.el = Roo.get(el);
16479     this.el.addClass('x-tree');
16480     //console.log(root);
16481     if (root) {
16482         this.setRootNode( Roo.factory(root, Roo.tree));
16483     }
16484     if (loader) {
16485         this.loader = Roo.factory(loader, Roo.tree);
16486     }
16487    /**
16488     * Read-only. The id of the container element becomes this TreePanel's id.
16489     */
16490     this.id = this.el.id;
16491     this.addEvents({
16492         /**
16493         * @event beforeload
16494         * Fires before a node is loaded, return false to cancel
16495         * @param {Node} node The node being loaded
16496         */
16497         "beforeload" : true,
16498         /**
16499         * @event load
16500         * Fires when a node is loaded
16501         * @param {Node} node The node that was loaded
16502         */
16503         "load" : true,
16504         /**
16505         * @event textchange
16506         * Fires when the text for a node is changed
16507         * @param {Node} node The node
16508         * @param {String} text The new text
16509         * @param {String} oldText The old text
16510         */
16511         "textchange" : true,
16512         /**
16513         * @event beforeexpand
16514         * Fires before a node is expanded, return false to cancel.
16515         * @param {Node} node The node
16516         * @param {Boolean} deep
16517         * @param {Boolean} anim
16518         */
16519         "beforeexpand" : true,
16520         /**
16521         * @event beforecollapse
16522         * Fires before a node is collapsed, return false to cancel.
16523         * @param {Node} node The node
16524         * @param {Boolean} deep
16525         * @param {Boolean} anim
16526         */
16527         "beforecollapse" : true,
16528         /**
16529         * @event expand
16530         * Fires when a node is expanded
16531         * @param {Node} node The node
16532         */
16533         "expand" : true,
16534         /**
16535         * @event disabledchange
16536         * Fires when the disabled status of a node changes
16537         * @param {Node} node The node
16538         * @param {Boolean} disabled
16539         */
16540         "disabledchange" : true,
16541         /**
16542         * @event collapse
16543         * Fires when a node is collapsed
16544         * @param {Node} node The node
16545         */
16546         "collapse" : true,
16547         /**
16548         * @event beforeclick
16549         * Fires before click processing on a node. Return false to cancel the default action.
16550         * @param {Node} node The node
16551         * @param {Roo.EventObject} e The event object
16552         */
16553         "beforeclick":true,
16554         /**
16555         * @event checkchange
16556         * Fires when a node with a checkbox's checked property changes
16557         * @param {Node} this This node
16558         * @param {Boolean} checked
16559         */
16560         "checkchange":true,
16561         /**
16562         * @event click
16563         * Fires when a node is clicked
16564         * @param {Node} node The node
16565         * @param {Roo.EventObject} e The event object
16566         */
16567         "click":true,
16568         /**
16569         * @event dblclick
16570         * Fires when a node is double clicked
16571         * @param {Node} node The node
16572         * @param {Roo.EventObject} e The event object
16573         */
16574         "dblclick":true,
16575         /**
16576         * @event contextmenu
16577         * Fires when a node is right clicked
16578         * @param {Node} node The node
16579         * @param {Roo.EventObject} e The event object
16580         */
16581         "contextmenu":true,
16582         /**
16583         * @event beforechildrenrendered
16584         * Fires right before the child nodes for a node are rendered
16585         * @param {Node} node The node
16586         */
16587         "beforechildrenrendered":true,
16588         /**
16589         * @event startdrag
16590         * Fires when a node starts being dragged
16591         * @param {Roo.tree.TreePanel} this
16592         * @param {Roo.tree.TreeNode} node
16593         * @param {event} e The raw browser event
16594         */ 
16595        "startdrag" : true,
16596        /**
16597         * @event enddrag
16598         * Fires when a drag operation is complete
16599         * @param {Roo.tree.TreePanel} this
16600         * @param {Roo.tree.TreeNode} node
16601         * @param {event} e The raw browser event
16602         */
16603        "enddrag" : true,
16604        /**
16605         * @event dragdrop
16606         * Fires when a dragged node is dropped on a valid DD target
16607         * @param {Roo.tree.TreePanel} this
16608         * @param {Roo.tree.TreeNode} node
16609         * @param {DD} dd The dd it was dropped on
16610         * @param {event} e The raw browser event
16611         */
16612        "dragdrop" : true,
16613        /**
16614         * @event beforenodedrop
16615         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16616         * passed to handlers has the following properties:<br />
16617         * <ul style="padding:5px;padding-left:16px;">
16618         * <li>tree - The TreePanel</li>
16619         * <li>target - The node being targeted for the drop</li>
16620         * <li>data - The drag data from the drag source</li>
16621         * <li>point - The point of the drop - append, above or below</li>
16622         * <li>source - The drag source</li>
16623         * <li>rawEvent - Raw mouse event</li>
16624         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16625         * to be inserted by setting them on this object.</li>
16626         * <li>cancel - Set this to true to cancel the drop.</li>
16627         * </ul>
16628         * @param {Object} dropEvent
16629         */
16630        "beforenodedrop" : true,
16631        /**
16632         * @event nodedrop
16633         * Fires after a DD object is dropped on a node in this tree. The dropEvent
16634         * passed to handlers has the following properties:<br />
16635         * <ul style="padding:5px;padding-left:16px;">
16636         * <li>tree - The TreePanel</li>
16637         * <li>target - The node being targeted for the drop</li>
16638         * <li>data - The drag data from the drag source</li>
16639         * <li>point - The point of the drop - append, above or below</li>
16640         * <li>source - The drag source</li>
16641         * <li>rawEvent - Raw mouse event</li>
16642         * <li>dropNode - Dropped node(s).</li>
16643         * </ul>
16644         * @param {Object} dropEvent
16645         */
16646        "nodedrop" : true,
16647         /**
16648         * @event nodedragover
16649         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16650         * passed to handlers has the following properties:<br />
16651         * <ul style="padding:5px;padding-left:16px;">
16652         * <li>tree - The TreePanel</li>
16653         * <li>target - The node being targeted for the drop</li>
16654         * <li>data - The drag data from the drag source</li>
16655         * <li>point - The point of the drop - append, above or below</li>
16656         * <li>source - The drag source</li>
16657         * <li>rawEvent - Raw mouse event</li>
16658         * <li>dropNode - Drop node(s) provided by the source.</li>
16659         * <li>cancel - Set this to true to signal drop not allowed.</li>
16660         * </ul>
16661         * @param {Object} dragOverEvent
16662         */
16663        "nodedragover" : true
16664         
16665     });
16666     if(this.singleExpand){
16667        this.on("beforeexpand", this.restrictExpand, this);
16668     }
16669     if (this.editor) {
16670         this.editor.tree = this;
16671         this.editor = Roo.factory(this.editor, Roo.tree);
16672     }
16673     
16674     if (this.selModel) {
16675         this.selModel = Roo.factory(this.selModel, Roo.tree);
16676     }
16677    
16678 };
16679 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16680     rootVisible : true,
16681     animate: Roo.enableFx,
16682     lines : true,
16683     enableDD : false,
16684     hlDrop : Roo.enableFx,
16685   
16686     renderer: false,
16687     
16688     rendererTip: false,
16689     // private
16690     restrictExpand : function(node){
16691         var p = node.parentNode;
16692         if(p){
16693             if(p.expandedChild && p.expandedChild.parentNode == p){
16694                 p.expandedChild.collapse();
16695             }
16696             p.expandedChild = node;
16697         }
16698     },
16699
16700     // private override
16701     setRootNode : function(node){
16702         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16703         if(!this.rootVisible){
16704             node.ui = new Roo.tree.RootTreeNodeUI(node);
16705         }
16706         return node;
16707     },
16708
16709     /**
16710      * Returns the container element for this TreePanel
16711      */
16712     getEl : function(){
16713         return this.el;
16714     },
16715
16716     /**
16717      * Returns the default TreeLoader for this TreePanel
16718      */
16719     getLoader : function(){
16720         return this.loader;
16721     },
16722
16723     /**
16724      * Expand all nodes
16725      */
16726     expandAll : function(){
16727         this.root.expand(true);
16728     },
16729
16730     /**
16731      * Collapse all nodes
16732      */
16733     collapseAll : function(){
16734         this.root.collapse(true);
16735     },
16736
16737     /**
16738      * Returns the selection model used by this TreePanel
16739      */
16740     getSelectionModel : function(){
16741         if(!this.selModel){
16742             this.selModel = new Roo.tree.DefaultSelectionModel();
16743         }
16744         return this.selModel;
16745     },
16746
16747     /**
16748      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16749      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16750      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16751      * @return {Array}
16752      */
16753     getChecked : function(a, startNode){
16754         startNode = startNode || this.root;
16755         var r = [];
16756         var f = function(){
16757             if(this.attributes.checked){
16758                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16759             }
16760         }
16761         startNode.cascade(f);
16762         return r;
16763     },
16764
16765     /**
16766      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16767      * @param {String} path
16768      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16769      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16770      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16771      */
16772     expandPath : function(path, attr, callback){
16773         attr = attr || "id";
16774         var keys = path.split(this.pathSeparator);
16775         var curNode = this.root;
16776         if(curNode.attributes[attr] != keys[1]){ // invalid root
16777             if(callback){
16778                 callback(false, null);
16779             }
16780             return;
16781         }
16782         var index = 1;
16783         var f = function(){
16784             if(++index == keys.length){
16785                 if(callback){
16786                     callback(true, curNode);
16787                 }
16788                 return;
16789             }
16790             var c = curNode.findChild(attr, keys[index]);
16791             if(!c){
16792                 if(callback){
16793                     callback(false, curNode);
16794                 }
16795                 return;
16796             }
16797             curNode = c;
16798             c.expand(false, false, f);
16799         };
16800         curNode.expand(false, false, f);
16801     },
16802
16803     /**
16804      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16805      * @param {String} path
16806      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16807      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16808      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16809      */
16810     selectPath : function(path, attr, callback){
16811         attr = attr || "id";
16812         var keys = path.split(this.pathSeparator);
16813         var v = keys.pop();
16814         if(keys.length > 0){
16815             var f = function(success, node){
16816                 if(success && node){
16817                     var n = node.findChild(attr, v);
16818                     if(n){
16819                         n.select();
16820                         if(callback){
16821                             callback(true, n);
16822                         }
16823                     }else if(callback){
16824                         callback(false, n);
16825                     }
16826                 }else{
16827                     if(callback){
16828                         callback(false, n);
16829                     }
16830                 }
16831             };
16832             this.expandPath(keys.join(this.pathSeparator), attr, f);
16833         }else{
16834             this.root.select();
16835             if(callback){
16836                 callback(true, this.root);
16837             }
16838         }
16839     },
16840
16841     getTreeEl : function(){
16842         return this.el;
16843     },
16844
16845     /**
16846      * Trigger rendering of this TreePanel
16847      */
16848     render : function(){
16849         if (this.innerCt) {
16850             return this; // stop it rendering more than once!!
16851         }
16852         
16853         this.innerCt = this.el.createChild({tag:"ul",
16854                cls:"x-tree-root-ct " +
16855                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
16856
16857         if(this.containerScroll){
16858             Roo.dd.ScrollManager.register(this.el);
16859         }
16860         if((this.enableDD || this.enableDrop) && !this.dropZone){
16861            /**
16862             * The dropZone used by this tree if drop is enabled
16863             * @type Roo.tree.TreeDropZone
16864             */
16865              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
16866                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
16867            });
16868         }
16869         if((this.enableDD || this.enableDrag) && !this.dragZone){
16870            /**
16871             * The dragZone used by this tree if drag is enabled
16872             * @type Roo.tree.TreeDragZone
16873             */
16874             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
16875                ddGroup: this.ddGroup || "TreeDD",
16876                scroll: this.ddScroll
16877            });
16878         }
16879         this.getSelectionModel().init(this);
16880         if (!this.root) {
16881             console.log("ROOT not set in tree");
16882             return;
16883         }
16884         this.root.render();
16885         if(!this.rootVisible){
16886             this.root.renderChildren();
16887         }
16888         return this;
16889     }
16890 });/*
16891  * Based on:
16892  * Ext JS Library 1.1.1
16893  * Copyright(c) 2006-2007, Ext JS, LLC.
16894  *
16895  * Originally Released Under LGPL - original licence link has changed is not relivant.
16896  *
16897  * Fork - LGPL
16898  * <script type="text/javascript">
16899  */
16900  
16901
16902 /**
16903  * @class Roo.tree.DefaultSelectionModel
16904  * @extends Roo.util.Observable
16905  * The default single selection for a TreePanel.
16906  * @param {Object} cfg Configuration
16907  */
16908 Roo.tree.DefaultSelectionModel = function(cfg){
16909    this.selNode = null;
16910    
16911    
16912    
16913    this.addEvents({
16914        /**
16915         * @event selectionchange
16916         * Fires when the selected node changes
16917         * @param {DefaultSelectionModel} this
16918         * @param {TreeNode} node the new selection
16919         */
16920        "selectionchange" : true,
16921
16922        /**
16923         * @event beforeselect
16924         * Fires before the selected node changes, return false to cancel the change
16925         * @param {DefaultSelectionModel} this
16926         * @param {TreeNode} node the new selection
16927         * @param {TreeNode} node the old selection
16928         */
16929        "beforeselect" : true
16930    });
16931    
16932     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
16933 };
16934
16935 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
16936     init : function(tree){
16937         this.tree = tree;
16938         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16939         tree.on("click", this.onNodeClick, this);
16940     },
16941     
16942     onNodeClick : function(node, e){
16943         if (e.ctrlKey && this.selNode == node)  {
16944             this.unselect(node);
16945             return;
16946         }
16947         this.select(node);
16948     },
16949     
16950     /**
16951      * Select a node.
16952      * @param {TreeNode} node The node to select
16953      * @return {TreeNode} The selected node
16954      */
16955     select : function(node){
16956         var last = this.selNode;
16957         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
16958             if(last){
16959                 last.ui.onSelectedChange(false);
16960             }
16961             this.selNode = node;
16962             node.ui.onSelectedChange(true);
16963             this.fireEvent("selectionchange", this, node, last);
16964         }
16965         return node;
16966     },
16967     
16968     /**
16969      * Deselect a node.
16970      * @param {TreeNode} node The node to unselect
16971      */
16972     unselect : function(node){
16973         if(this.selNode == node){
16974             this.clearSelections();
16975         }    
16976     },
16977     
16978     /**
16979      * Clear all selections
16980      */
16981     clearSelections : function(){
16982         var n = this.selNode;
16983         if(n){
16984             n.ui.onSelectedChange(false);
16985             this.selNode = null;
16986             this.fireEvent("selectionchange", this, null);
16987         }
16988         return n;
16989     },
16990     
16991     /**
16992      * Get the selected node
16993      * @return {TreeNode} The selected node
16994      */
16995     getSelectedNode : function(){
16996         return this.selNode;    
16997     },
16998     
16999     /**
17000      * Returns true if the node is selected
17001      * @param {TreeNode} node The node to check
17002      * @return {Boolean}
17003      */
17004     isSelected : function(node){
17005         return this.selNode == node;  
17006     },
17007
17008     /**
17009      * Selects the node above the selected node in the tree, intelligently walking the nodes
17010      * @return TreeNode The new selection
17011      */
17012     selectPrevious : function(){
17013         var s = this.selNode || this.lastSelNode;
17014         if(!s){
17015             return null;
17016         }
17017         var ps = s.previousSibling;
17018         if(ps){
17019             if(!ps.isExpanded() || ps.childNodes.length < 1){
17020                 return this.select(ps);
17021             } else{
17022                 var lc = ps.lastChild;
17023                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
17024                     lc = lc.lastChild;
17025                 }
17026                 return this.select(lc);
17027             }
17028         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
17029             return this.select(s.parentNode);
17030         }
17031         return null;
17032     },
17033
17034     /**
17035      * Selects the node above the selected node in the tree, intelligently walking the nodes
17036      * @return TreeNode The new selection
17037      */
17038     selectNext : function(){
17039         var s = this.selNode || this.lastSelNode;
17040         if(!s){
17041             return null;
17042         }
17043         if(s.firstChild && s.isExpanded()){
17044              return this.select(s.firstChild);
17045          }else if(s.nextSibling){
17046              return this.select(s.nextSibling);
17047          }else if(s.parentNode){
17048             var newS = null;
17049             s.parentNode.bubble(function(){
17050                 if(this.nextSibling){
17051                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
17052                     return false;
17053                 }
17054             });
17055             return newS;
17056          }
17057         return null;
17058     },
17059
17060     onKeyDown : function(e){
17061         var s = this.selNode || this.lastSelNode;
17062         // undesirable, but required
17063         var sm = this;
17064         if(!s){
17065             return;
17066         }
17067         var k = e.getKey();
17068         switch(k){
17069              case e.DOWN:
17070                  e.stopEvent();
17071                  this.selectNext();
17072              break;
17073              case e.UP:
17074                  e.stopEvent();
17075                  this.selectPrevious();
17076              break;
17077              case e.RIGHT:
17078                  e.preventDefault();
17079                  if(s.hasChildNodes()){
17080                      if(!s.isExpanded()){
17081                          s.expand();
17082                      }else if(s.firstChild){
17083                          this.select(s.firstChild, e);
17084                      }
17085                  }
17086              break;
17087              case e.LEFT:
17088                  e.preventDefault();
17089                  if(s.hasChildNodes() && s.isExpanded()){
17090                      s.collapse();
17091                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
17092                      this.select(s.parentNode, e);
17093                  }
17094              break;
17095         };
17096     }
17097 });
17098
17099 /**
17100  * @class Roo.tree.MultiSelectionModel
17101  * @extends Roo.util.Observable
17102  * Multi selection for a TreePanel.
17103  * @param {Object} cfg Configuration
17104  */
17105 Roo.tree.MultiSelectionModel = function(){
17106    this.selNodes = [];
17107    this.selMap = {};
17108    this.addEvents({
17109        /**
17110         * @event selectionchange
17111         * Fires when the selected nodes change
17112         * @param {MultiSelectionModel} this
17113         * @param {Array} nodes Array of the selected nodes
17114         */
17115        "selectionchange" : true
17116    });
17117    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
17118    
17119 };
17120
17121 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
17122     init : function(tree){
17123         this.tree = tree;
17124         tree.getTreeEl().on("keydown", this.onKeyDown, this);
17125         tree.on("click", this.onNodeClick, this);
17126     },
17127     
17128     onNodeClick : function(node, e){
17129         this.select(node, e, e.ctrlKey);
17130     },
17131     
17132     /**
17133      * Select a node.
17134      * @param {TreeNode} node The node to select
17135      * @param {EventObject} e (optional) An event associated with the selection
17136      * @param {Boolean} keepExisting True to retain existing selections
17137      * @return {TreeNode} The selected node
17138      */
17139     select : function(node, e, keepExisting){
17140         if(keepExisting !== true){
17141             this.clearSelections(true);
17142         }
17143         if(this.isSelected(node)){
17144             this.lastSelNode = node;
17145             return node;
17146         }
17147         this.selNodes.push(node);
17148         this.selMap[node.id] = node;
17149         this.lastSelNode = node;
17150         node.ui.onSelectedChange(true);
17151         this.fireEvent("selectionchange", this, this.selNodes);
17152         return node;
17153     },
17154     
17155     /**
17156      * Deselect a node.
17157      * @param {TreeNode} node The node to unselect
17158      */
17159     unselect : function(node){
17160         if(this.selMap[node.id]){
17161             node.ui.onSelectedChange(false);
17162             var sn = this.selNodes;
17163             var index = -1;
17164             if(sn.indexOf){
17165                 index = sn.indexOf(node);
17166             }else{
17167                 for(var i = 0, len = sn.length; i < len; i++){
17168                     if(sn[i] == node){
17169                         index = i;
17170                         break;
17171                     }
17172                 }
17173             }
17174             if(index != -1){
17175                 this.selNodes.splice(index, 1);
17176             }
17177             delete this.selMap[node.id];
17178             this.fireEvent("selectionchange", this, this.selNodes);
17179         }
17180     },
17181     
17182     /**
17183      * Clear all selections
17184      */
17185     clearSelections : function(suppressEvent){
17186         var sn = this.selNodes;
17187         if(sn.length > 0){
17188             for(var i = 0, len = sn.length; i < len; i++){
17189                 sn[i].ui.onSelectedChange(false);
17190             }
17191             this.selNodes = [];
17192             this.selMap = {};
17193             if(suppressEvent !== true){
17194                 this.fireEvent("selectionchange", this, this.selNodes);
17195             }
17196         }
17197     },
17198     
17199     /**
17200      * Returns true if the node is selected
17201      * @param {TreeNode} node The node to check
17202      * @return {Boolean}
17203      */
17204     isSelected : function(node){
17205         return this.selMap[node.id] ? true : false;  
17206     },
17207     
17208     /**
17209      * Returns an array of the selected nodes
17210      * @return {Array}
17211      */
17212     getSelectedNodes : function(){
17213         return this.selNodes;    
17214     },
17215
17216     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
17217
17218     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
17219
17220     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
17221 });/*
17222  * Based on:
17223  * Ext JS Library 1.1.1
17224  * Copyright(c) 2006-2007, Ext JS, LLC.
17225  *
17226  * Originally Released Under LGPL - original licence link has changed is not relivant.
17227  *
17228  * Fork - LGPL
17229  * <script type="text/javascript">
17230  */
17231  
17232 /**
17233  * @class Roo.tree.TreeNode
17234  * @extends Roo.data.Node
17235  * @cfg {String} text The text for this node
17236  * @cfg {Boolean} expanded true to start the node expanded
17237  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
17238  * @cfg {Boolean} allowDrop false if this node cannot be drop on
17239  * @cfg {Boolean} disabled true to start the node disabled
17240  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
17241  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
17242  * @cfg {String} cls A css class to be added to the node
17243  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
17244  * @cfg {String} href URL of the link used for the node (defaults to #)
17245  * @cfg {String} hrefTarget target frame for the link
17246  * @cfg {String} qtip An Ext QuickTip for the node
17247  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
17248  * @cfg {Boolean} singleClickExpand True for single click expand on this node
17249  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
17250  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
17251  * (defaults to undefined with no checkbox rendered)
17252  * @constructor
17253  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
17254  */
17255 Roo.tree.TreeNode = function(attributes){
17256     attributes = attributes || {};
17257     if(typeof attributes == "string"){
17258         attributes = {text: attributes};
17259     }
17260     this.childrenRendered = false;
17261     this.rendered = false;
17262     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
17263     this.expanded = attributes.expanded === true;
17264     this.isTarget = attributes.isTarget !== false;
17265     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
17266     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
17267
17268     /**
17269      * Read-only. The text for this node. To change it use setText().
17270      * @type String
17271      */
17272     this.text = attributes.text;
17273     /**
17274      * True if this node is disabled.
17275      * @type Boolean
17276      */
17277     this.disabled = attributes.disabled === true;
17278
17279     this.addEvents({
17280         /**
17281         * @event textchange
17282         * Fires when the text for this node is changed
17283         * @param {Node} this This node
17284         * @param {String} text The new text
17285         * @param {String} oldText The old text
17286         */
17287         "textchange" : true,
17288         /**
17289         * @event beforeexpand
17290         * Fires before this node is expanded, return false to cancel.
17291         * @param {Node} this This node
17292         * @param {Boolean} deep
17293         * @param {Boolean} anim
17294         */
17295         "beforeexpand" : true,
17296         /**
17297         * @event beforecollapse
17298         * Fires before this node is collapsed, return false to cancel.
17299         * @param {Node} this This node
17300         * @param {Boolean} deep
17301         * @param {Boolean} anim
17302         */
17303         "beforecollapse" : true,
17304         /**
17305         * @event expand
17306         * Fires when this node is expanded
17307         * @param {Node} this This node
17308         */
17309         "expand" : true,
17310         /**
17311         * @event disabledchange
17312         * Fires when the disabled status of this node changes
17313         * @param {Node} this This node
17314         * @param {Boolean} disabled
17315         */
17316         "disabledchange" : true,
17317         /**
17318         * @event collapse
17319         * Fires when this node is collapsed
17320         * @param {Node} this This node
17321         */
17322         "collapse" : true,
17323         /**
17324         * @event beforeclick
17325         * Fires before click processing. Return false to cancel the default action.
17326         * @param {Node} this This node
17327         * @param {Roo.EventObject} e The event object
17328         */
17329         "beforeclick":true,
17330         /**
17331         * @event checkchange
17332         * Fires when a node with a checkbox's checked property changes
17333         * @param {Node} this This node
17334         * @param {Boolean} checked
17335         */
17336         "checkchange":true,
17337         /**
17338         * @event click
17339         * Fires when this node is clicked
17340         * @param {Node} this This node
17341         * @param {Roo.EventObject} e The event object
17342         */
17343         "click":true,
17344         /**
17345         * @event dblclick
17346         * Fires when this node is double clicked
17347         * @param {Node} this This node
17348         * @param {Roo.EventObject} e The event object
17349         */
17350         "dblclick":true,
17351         /**
17352         * @event contextmenu
17353         * Fires when this node is right clicked
17354         * @param {Node} this This node
17355         * @param {Roo.EventObject} e The event object
17356         */
17357         "contextmenu":true,
17358         /**
17359         * @event beforechildrenrendered
17360         * Fires right before the child nodes for this node are rendered
17361         * @param {Node} this This node
17362         */
17363         "beforechildrenrendered":true
17364     });
17365
17366     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
17367
17368     /**
17369      * Read-only. The UI for this node
17370      * @type TreeNodeUI
17371      */
17372     this.ui = new uiClass(this);
17373 };
17374 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
17375     preventHScroll: true,
17376     /**
17377      * Returns true if this node is expanded
17378      * @return {Boolean}
17379      */
17380     isExpanded : function(){
17381         return this.expanded;
17382     },
17383
17384     /**
17385      * Returns the UI object for this node
17386      * @return {TreeNodeUI}
17387      */
17388     getUI : function(){
17389         return this.ui;
17390     },
17391
17392     // private override
17393     setFirstChild : function(node){
17394         var of = this.firstChild;
17395         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
17396         if(this.childrenRendered && of && node != of){
17397             of.renderIndent(true, true);
17398         }
17399         if(this.rendered){
17400             this.renderIndent(true, true);
17401         }
17402     },
17403
17404     // private override
17405     setLastChild : function(node){
17406         var ol = this.lastChild;
17407         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
17408         if(this.childrenRendered && ol && node != ol){
17409             ol.renderIndent(true, true);
17410         }
17411         if(this.rendered){
17412             this.renderIndent(true, true);
17413         }
17414     },
17415
17416     // these methods are overridden to provide lazy rendering support
17417     // private override
17418     appendChild : function(){
17419         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
17420         if(node && this.childrenRendered){
17421             node.render();
17422         }
17423         this.ui.updateExpandIcon();
17424         return node;
17425     },
17426
17427     // private override
17428     removeChild : function(node){
17429         this.ownerTree.getSelectionModel().unselect(node);
17430         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
17431         // if it's been rendered remove dom node
17432         if(this.childrenRendered){
17433             node.ui.remove();
17434         }
17435         if(this.childNodes.length < 1){
17436             this.collapse(false, false);
17437         }else{
17438             this.ui.updateExpandIcon();
17439         }
17440         if(!this.firstChild) {
17441             this.childrenRendered = false;
17442         }
17443         return node;
17444     },
17445
17446     // private override
17447     insertBefore : function(node, refNode){
17448         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
17449         if(newNode && refNode && this.childrenRendered){
17450             node.render();
17451         }
17452         this.ui.updateExpandIcon();
17453         return newNode;
17454     },
17455
17456     /**
17457      * Sets the text for this node
17458      * @param {String} text
17459      */
17460     setText : function(text){
17461         var oldText = this.text;
17462         this.text = text;
17463         this.attributes.text = text;
17464         if(this.rendered){ // event without subscribing
17465             this.ui.onTextChange(this, text, oldText);
17466         }
17467         this.fireEvent("textchange", this, text, oldText);
17468     },
17469
17470     /**
17471      * Triggers selection of this node
17472      */
17473     select : function(){
17474         this.getOwnerTree().getSelectionModel().select(this);
17475     },
17476
17477     /**
17478      * Triggers deselection of this node
17479      */
17480     unselect : function(){
17481         this.getOwnerTree().getSelectionModel().unselect(this);
17482     },
17483
17484     /**
17485      * Returns true if this node is selected
17486      * @return {Boolean}
17487      */
17488     isSelected : function(){
17489         return this.getOwnerTree().getSelectionModel().isSelected(this);
17490     },
17491
17492     /**
17493      * Expand this node.
17494      * @param {Boolean} deep (optional) True to expand all children as well
17495      * @param {Boolean} anim (optional) false to cancel the default animation
17496      * @param {Function} callback (optional) A callback to be called when
17497      * expanding this node completes (does not wait for deep expand to complete).
17498      * Called with 1 parameter, this node.
17499      */
17500     expand : function(deep, anim, callback){
17501         if(!this.expanded){
17502             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
17503                 return;
17504             }
17505             if(!this.childrenRendered){
17506                 this.renderChildren();
17507             }
17508             this.expanded = true;
17509             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17510                 this.ui.animExpand(function(){
17511                     this.fireEvent("expand", this);
17512                     if(typeof callback == "function"){
17513                         callback(this);
17514                     }
17515                     if(deep === true){
17516                         this.expandChildNodes(true);
17517                     }
17518                 }.createDelegate(this));
17519                 return;
17520             }else{
17521                 this.ui.expand();
17522                 this.fireEvent("expand", this);
17523                 if(typeof callback == "function"){
17524                     callback(this);
17525                 }
17526             }
17527         }else{
17528            if(typeof callback == "function"){
17529                callback(this);
17530            }
17531         }
17532         if(deep === true){
17533             this.expandChildNodes(true);
17534         }
17535     },
17536
17537     isHiddenRoot : function(){
17538         return this.isRoot && !this.getOwnerTree().rootVisible;
17539     },
17540
17541     /**
17542      * Collapse this node.
17543      * @param {Boolean} deep (optional) True to collapse all children as well
17544      * @param {Boolean} anim (optional) false to cancel the default animation
17545      */
17546     collapse : function(deep, anim){
17547         if(this.expanded && !this.isHiddenRoot()){
17548             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17549                 return;
17550             }
17551             this.expanded = false;
17552             if((this.getOwnerTree().animate && anim !== false) || anim){
17553                 this.ui.animCollapse(function(){
17554                     this.fireEvent("collapse", this);
17555                     if(deep === true){
17556                         this.collapseChildNodes(true);
17557                     }
17558                 }.createDelegate(this));
17559                 return;
17560             }else{
17561                 this.ui.collapse();
17562                 this.fireEvent("collapse", this);
17563             }
17564         }
17565         if(deep === true){
17566             var cs = this.childNodes;
17567             for(var i = 0, len = cs.length; i < len; i++) {
17568                 cs[i].collapse(true, false);
17569             }
17570         }
17571     },
17572
17573     // private
17574     delayedExpand : function(delay){
17575         if(!this.expandProcId){
17576             this.expandProcId = this.expand.defer(delay, this);
17577         }
17578     },
17579
17580     // private
17581     cancelExpand : function(){
17582         if(this.expandProcId){
17583             clearTimeout(this.expandProcId);
17584         }
17585         this.expandProcId = false;
17586     },
17587
17588     /**
17589      * Toggles expanded/collapsed state of the node
17590      */
17591     toggle : function(){
17592         if(this.expanded){
17593             this.collapse();
17594         }else{
17595             this.expand();
17596         }
17597     },
17598
17599     /**
17600      * Ensures all parent nodes are expanded
17601      */
17602     ensureVisible : function(callback){
17603         var tree = this.getOwnerTree();
17604         tree.expandPath(this.parentNode.getPath(), false, function(){
17605             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17606             Roo.callback(callback);
17607         }.createDelegate(this));
17608     },
17609
17610     /**
17611      * Expand all child nodes
17612      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17613      */
17614     expandChildNodes : function(deep){
17615         var cs = this.childNodes;
17616         for(var i = 0, len = cs.length; i < len; i++) {
17617                 cs[i].expand(deep);
17618         }
17619     },
17620
17621     /**
17622      * Collapse all child nodes
17623      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17624      */
17625     collapseChildNodes : function(deep){
17626         var cs = this.childNodes;
17627         for(var i = 0, len = cs.length; i < len; i++) {
17628                 cs[i].collapse(deep);
17629         }
17630     },
17631
17632     /**
17633      * Disables this node
17634      */
17635     disable : function(){
17636         this.disabled = true;
17637         this.unselect();
17638         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17639             this.ui.onDisableChange(this, true);
17640         }
17641         this.fireEvent("disabledchange", this, true);
17642     },
17643
17644     /**
17645      * Enables this node
17646      */
17647     enable : function(){
17648         this.disabled = false;
17649         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17650             this.ui.onDisableChange(this, false);
17651         }
17652         this.fireEvent("disabledchange", this, false);
17653     },
17654
17655     // private
17656     renderChildren : function(suppressEvent){
17657         if(suppressEvent !== false){
17658             this.fireEvent("beforechildrenrendered", this);
17659         }
17660         var cs = this.childNodes;
17661         for(var i = 0, len = cs.length; i < len; i++){
17662             cs[i].render(true);
17663         }
17664         this.childrenRendered = true;
17665     },
17666
17667     // private
17668     sort : function(fn, scope){
17669         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17670         if(this.childrenRendered){
17671             var cs = this.childNodes;
17672             for(var i = 0, len = cs.length; i < len; i++){
17673                 cs[i].render(true);
17674             }
17675         }
17676     },
17677
17678     // private
17679     render : function(bulkRender){
17680         this.ui.render(bulkRender);
17681         if(!this.rendered){
17682             this.rendered = true;
17683             if(this.expanded){
17684                 this.expanded = false;
17685                 this.expand(false, false);
17686             }
17687         }
17688     },
17689
17690     // private
17691     renderIndent : function(deep, refresh){
17692         if(refresh){
17693             this.ui.childIndent = null;
17694         }
17695         this.ui.renderIndent();
17696         if(deep === true && this.childrenRendered){
17697             var cs = this.childNodes;
17698             for(var i = 0, len = cs.length; i < len; i++){
17699                 cs[i].renderIndent(true, refresh);
17700             }
17701         }
17702     }
17703 });/*
17704  * Based on:
17705  * Ext JS Library 1.1.1
17706  * Copyright(c) 2006-2007, Ext JS, LLC.
17707  *
17708  * Originally Released Under LGPL - original licence link has changed is not relivant.
17709  *
17710  * Fork - LGPL
17711  * <script type="text/javascript">
17712  */
17713  
17714 /**
17715  * @class Roo.tree.AsyncTreeNode
17716  * @extends Roo.tree.TreeNode
17717  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17718  * @constructor
17719  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
17720  */
17721  Roo.tree.AsyncTreeNode = function(config){
17722     this.loaded = false;
17723     this.loading = false;
17724     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17725     /**
17726     * @event beforeload
17727     * Fires before this node is loaded, return false to cancel
17728     * @param {Node} this This node
17729     */
17730     this.addEvents({'beforeload':true, 'load': true});
17731     /**
17732     * @event load
17733     * Fires when this node is loaded
17734     * @param {Node} this This node
17735     */
17736     /**
17737      * The loader used by this node (defaults to using the tree's defined loader)
17738      * @type TreeLoader
17739      * @property loader
17740      */
17741 };
17742 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17743     expand : function(deep, anim, callback){
17744         if(this.loading){ // if an async load is already running, waiting til it's done
17745             var timer;
17746             var f = function(){
17747                 if(!this.loading){ // done loading
17748                     clearInterval(timer);
17749                     this.expand(deep, anim, callback);
17750                 }
17751             }.createDelegate(this);
17752             timer = setInterval(f, 200);
17753             return;
17754         }
17755         if(!this.loaded){
17756             if(this.fireEvent("beforeload", this) === false){
17757                 return;
17758             }
17759             this.loading = true;
17760             this.ui.beforeLoad(this);
17761             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17762             if(loader){
17763                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17764                 return;
17765             }
17766         }
17767         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17768     },
17769     
17770     /**
17771      * Returns true if this node is currently loading
17772      * @return {Boolean}
17773      */
17774     isLoading : function(){
17775         return this.loading;  
17776     },
17777     
17778     loadComplete : function(deep, anim, callback){
17779         this.loading = false;
17780         this.loaded = true;
17781         this.ui.afterLoad(this);
17782         this.fireEvent("load", this);
17783         this.expand(deep, anim, callback);
17784     },
17785     
17786     /**
17787      * Returns true if this node has been loaded
17788      * @return {Boolean}
17789      */
17790     isLoaded : function(){
17791         return this.loaded;
17792     },
17793     
17794     hasChildNodes : function(){
17795         if(!this.isLeaf() && !this.loaded){
17796             return true;
17797         }else{
17798             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17799         }
17800     },
17801
17802     /**
17803      * Trigger a reload for this node
17804      * @param {Function} callback
17805      */
17806     reload : function(callback){
17807         this.collapse(false, false);
17808         while(this.firstChild){
17809             this.removeChild(this.firstChild);
17810         }
17811         this.childrenRendered = false;
17812         this.loaded = false;
17813         if(this.isHiddenRoot()){
17814             this.expanded = false;
17815         }
17816         this.expand(false, false, callback);
17817     }
17818 });/*
17819  * Based on:
17820  * Ext JS Library 1.1.1
17821  * Copyright(c) 2006-2007, Ext JS, LLC.
17822  *
17823  * Originally Released Under LGPL - original licence link has changed is not relivant.
17824  *
17825  * Fork - LGPL
17826  * <script type="text/javascript">
17827  */
17828  
17829 /**
17830  * @class Roo.tree.TreeNodeUI
17831  * @constructor
17832  * @param {Object} node The node to render
17833  * The TreeNode UI implementation is separate from the
17834  * tree implementation. Unless you are customizing the tree UI,
17835  * you should never have to use this directly.
17836  */
17837 Roo.tree.TreeNodeUI = function(node){
17838     this.node = node;
17839     this.rendered = false;
17840     this.animating = false;
17841     this.emptyIcon = Roo.BLANK_IMAGE_URL;
17842 };
17843
17844 Roo.tree.TreeNodeUI.prototype = {
17845     removeChild : function(node){
17846         if(this.rendered){
17847             this.ctNode.removeChild(node.ui.getEl());
17848         }
17849     },
17850
17851     beforeLoad : function(){
17852          this.addClass("x-tree-node-loading");
17853     },
17854
17855     afterLoad : function(){
17856          this.removeClass("x-tree-node-loading");
17857     },
17858
17859     onTextChange : function(node, text, oldText){
17860         if(this.rendered){
17861             this.textNode.innerHTML = text;
17862         }
17863     },
17864
17865     onDisableChange : function(node, state){
17866         this.disabled = state;
17867         if(state){
17868             this.addClass("x-tree-node-disabled");
17869         }else{
17870             this.removeClass("x-tree-node-disabled");
17871         }
17872     },
17873
17874     onSelectedChange : function(state){
17875         if(state){
17876             this.focus();
17877             this.addClass("x-tree-selected");
17878         }else{
17879             //this.blur();
17880             this.removeClass("x-tree-selected");
17881         }
17882     },
17883
17884     onMove : function(tree, node, oldParent, newParent, index, refNode){
17885         this.childIndent = null;
17886         if(this.rendered){
17887             var targetNode = newParent.ui.getContainer();
17888             if(!targetNode){//target not rendered
17889                 this.holder = document.createElement("div");
17890                 this.holder.appendChild(this.wrap);
17891                 return;
17892             }
17893             var insertBefore = refNode ? refNode.ui.getEl() : null;
17894             if(insertBefore){
17895                 targetNode.insertBefore(this.wrap, insertBefore);
17896             }else{
17897                 targetNode.appendChild(this.wrap);
17898             }
17899             this.node.renderIndent(true);
17900         }
17901     },
17902
17903     addClass : function(cls){
17904         if(this.elNode){
17905             Roo.fly(this.elNode).addClass(cls);
17906         }
17907     },
17908
17909     removeClass : function(cls){
17910         if(this.elNode){
17911             Roo.fly(this.elNode).removeClass(cls);
17912         }
17913     },
17914
17915     remove : function(){
17916         if(this.rendered){
17917             this.holder = document.createElement("div");
17918             this.holder.appendChild(this.wrap);
17919         }
17920     },
17921
17922     fireEvent : function(){
17923         return this.node.fireEvent.apply(this.node, arguments);
17924     },
17925
17926     initEvents : function(){
17927         this.node.on("move", this.onMove, this);
17928         var E = Roo.EventManager;
17929         var a = this.anchor;
17930
17931         var el = Roo.fly(a, '_treeui');
17932
17933         if(Roo.isOpera){ // opera render bug ignores the CSS
17934             el.setStyle("text-decoration", "none");
17935         }
17936
17937         el.on("click", this.onClick, this);
17938         el.on("dblclick", this.onDblClick, this);
17939
17940         if(this.checkbox){
17941             Roo.EventManager.on(this.checkbox,
17942                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
17943         }
17944
17945         el.on("contextmenu", this.onContextMenu, this);
17946
17947         var icon = Roo.fly(this.iconNode);
17948         icon.on("click", this.onClick, this);
17949         icon.on("dblclick", this.onDblClick, this);
17950         icon.on("contextmenu", this.onContextMenu, this);
17951         E.on(this.ecNode, "click", this.ecClick, this, true);
17952
17953         if(this.node.disabled){
17954             this.addClass("x-tree-node-disabled");
17955         }
17956         if(this.node.hidden){
17957             this.addClass("x-tree-node-disabled");
17958         }
17959         var ot = this.node.getOwnerTree();
17960         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
17961         if(dd && (!this.node.isRoot || ot.rootVisible)){
17962             Roo.dd.Registry.register(this.elNode, {
17963                 node: this.node,
17964                 handles: this.getDDHandles(),
17965                 isHandle: false
17966             });
17967         }
17968     },
17969
17970     getDDHandles : function(){
17971         return [this.iconNode, this.textNode];
17972     },
17973
17974     hide : function(){
17975         if(this.rendered){
17976             this.wrap.style.display = "none";
17977         }
17978     },
17979
17980     show : function(){
17981         if(this.rendered){
17982             this.wrap.style.display = "";
17983         }
17984     },
17985
17986     onContextMenu : function(e){
17987         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
17988             e.preventDefault();
17989             this.focus();
17990             this.fireEvent("contextmenu", this.node, e);
17991         }
17992     },
17993
17994     onClick : function(e){
17995         if(this.dropping){
17996             e.stopEvent();
17997             return;
17998         }
17999         if(this.fireEvent("beforeclick", this.node, e) !== false){
18000             if(!this.disabled && this.node.attributes.href){
18001                 this.fireEvent("click", this.node, e);
18002                 return;
18003             }
18004             e.preventDefault();
18005             if(this.disabled){
18006                 return;
18007             }
18008
18009             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
18010                 this.node.toggle();
18011             }
18012
18013             this.fireEvent("click", this.node, e);
18014         }else{
18015             e.stopEvent();
18016         }
18017     },
18018
18019     onDblClick : function(e){
18020         e.preventDefault();
18021         if(this.disabled){
18022             return;
18023         }
18024         if(this.checkbox){
18025             this.toggleCheck();
18026         }
18027         if(!this.animating && this.node.hasChildNodes()){
18028             this.node.toggle();
18029         }
18030         this.fireEvent("dblclick", this.node, e);
18031     },
18032
18033     onCheckChange : function(){
18034         var checked = this.checkbox.checked;
18035         this.node.attributes.checked = checked;
18036         this.fireEvent('checkchange', this.node, checked);
18037     },
18038
18039     ecClick : function(e){
18040         if(!this.animating && this.node.hasChildNodes()){
18041             this.node.toggle();
18042         }
18043     },
18044
18045     startDrop : function(){
18046         this.dropping = true;
18047     },
18048
18049     // delayed drop so the click event doesn't get fired on a drop
18050     endDrop : function(){
18051        setTimeout(function(){
18052            this.dropping = false;
18053        }.createDelegate(this), 50);
18054     },
18055
18056     expand : function(){
18057         this.updateExpandIcon();
18058         this.ctNode.style.display = "";
18059     },
18060
18061     focus : function(){
18062         if(!this.node.preventHScroll){
18063             try{this.anchor.focus();
18064             }catch(e){}
18065         }else if(!Roo.isIE){
18066             try{
18067                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
18068                 var l = noscroll.scrollLeft;
18069                 this.anchor.focus();
18070                 noscroll.scrollLeft = l;
18071             }catch(e){}
18072         }
18073     },
18074
18075     toggleCheck : function(value){
18076         var cb = this.checkbox;
18077         if(cb){
18078             cb.checked = (value === undefined ? !cb.checked : value);
18079         }
18080     },
18081
18082     blur : function(){
18083         try{
18084             this.anchor.blur();
18085         }catch(e){}
18086     },
18087
18088     animExpand : function(callback){
18089         var ct = Roo.get(this.ctNode);
18090         ct.stopFx();
18091         if(!this.node.hasChildNodes()){
18092             this.updateExpandIcon();
18093             this.ctNode.style.display = "";
18094             Roo.callback(callback);
18095             return;
18096         }
18097         this.animating = true;
18098         this.updateExpandIcon();
18099
18100         ct.slideIn('t', {
18101            callback : function(){
18102                this.animating = false;
18103                Roo.callback(callback);
18104             },
18105             scope: this,
18106             duration: this.node.ownerTree.duration || .25
18107         });
18108     },
18109
18110     highlight : function(){
18111         var tree = this.node.getOwnerTree();
18112         Roo.fly(this.wrap).highlight(
18113             tree.hlColor || "C3DAF9",
18114             {endColor: tree.hlBaseColor}
18115         );
18116     },
18117
18118     collapse : function(){
18119         this.updateExpandIcon();
18120         this.ctNode.style.display = "none";
18121     },
18122
18123     animCollapse : function(callback){
18124         var ct = Roo.get(this.ctNode);
18125         ct.enableDisplayMode('block');
18126         ct.stopFx();
18127
18128         this.animating = true;
18129         this.updateExpandIcon();
18130
18131         ct.slideOut('t', {
18132             callback : function(){
18133                this.animating = false;
18134                Roo.callback(callback);
18135             },
18136             scope: this,
18137             duration: this.node.ownerTree.duration || .25
18138         });
18139     },
18140
18141     getContainer : function(){
18142         return this.ctNode;
18143     },
18144
18145     getEl : function(){
18146         return this.wrap;
18147     },
18148
18149     appendDDGhost : function(ghostNode){
18150         ghostNode.appendChild(this.elNode.cloneNode(true));
18151     },
18152
18153     getDDRepairXY : function(){
18154         return Roo.lib.Dom.getXY(this.iconNode);
18155     },
18156
18157     onRender : function(){
18158         this.render();
18159     },
18160
18161     render : function(bulkRender){
18162         var n = this.node, a = n.attributes;
18163         var targetNode = n.parentNode ?
18164               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
18165
18166         if(!this.rendered){
18167             this.rendered = true;
18168
18169             this.renderElements(n, a, targetNode, bulkRender);
18170
18171             if(a.qtip){
18172                if(this.textNode.setAttributeNS){
18173                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
18174                    if(a.qtipTitle){
18175                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
18176                    }
18177                }else{
18178                    this.textNode.setAttribute("ext:qtip", a.qtip);
18179                    if(a.qtipTitle){
18180                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
18181                    }
18182                }
18183             }else if(a.qtipCfg){
18184                 a.qtipCfg.target = Roo.id(this.textNode);
18185                 Roo.QuickTips.register(a.qtipCfg);
18186             }
18187             this.initEvents();
18188             if(!this.node.expanded){
18189                 this.updateExpandIcon();
18190             }
18191         }else{
18192             if(bulkRender === true) {
18193                 targetNode.appendChild(this.wrap);
18194             }
18195         }
18196     },
18197
18198     renderElements : function(n, a, targetNode, bulkRender)
18199     {
18200         // add some indent caching, this helps performance when rendering a large tree
18201         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18202         var t = n.getOwnerTree();
18203         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
18204         if (typeof(n.attributes.html) != 'undefined') {
18205             txt = n.attributes.html;
18206         }
18207         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
18208         var cb = typeof a.checked == 'boolean';
18209         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18210         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
18211             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
18212             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
18213             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
18214             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
18215             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
18216              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
18217                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
18218             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18219             "</li>"];
18220
18221         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18222             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18223                                 n.nextSibling.ui.getEl(), buf.join(""));
18224         }else{
18225             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18226         }
18227
18228         this.elNode = this.wrap.childNodes[0];
18229         this.ctNode = this.wrap.childNodes[1];
18230         var cs = this.elNode.childNodes;
18231         this.indentNode = cs[0];
18232         this.ecNode = cs[1];
18233         this.iconNode = cs[2];
18234         var index = 3;
18235         if(cb){
18236             this.checkbox = cs[3];
18237             index++;
18238         }
18239         this.anchor = cs[index];
18240         this.textNode = cs[index].firstChild;
18241     },
18242
18243     getAnchor : function(){
18244         return this.anchor;
18245     },
18246
18247     getTextEl : function(){
18248         return this.textNode;
18249     },
18250
18251     getIconEl : function(){
18252         return this.iconNode;
18253     },
18254
18255     isChecked : function(){
18256         return this.checkbox ? this.checkbox.checked : false;
18257     },
18258
18259     updateExpandIcon : function(){
18260         if(this.rendered){
18261             var n = this.node, c1, c2;
18262             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
18263             var hasChild = n.hasChildNodes();
18264             if(hasChild){
18265                 if(n.expanded){
18266                     cls += "-minus";
18267                     c1 = "x-tree-node-collapsed";
18268                     c2 = "x-tree-node-expanded";
18269                 }else{
18270                     cls += "-plus";
18271                     c1 = "x-tree-node-expanded";
18272                     c2 = "x-tree-node-collapsed";
18273                 }
18274                 if(this.wasLeaf){
18275                     this.removeClass("x-tree-node-leaf");
18276                     this.wasLeaf = false;
18277                 }
18278                 if(this.c1 != c1 || this.c2 != c2){
18279                     Roo.fly(this.elNode).replaceClass(c1, c2);
18280                     this.c1 = c1; this.c2 = c2;
18281                 }
18282             }else{
18283                 // this changes non-leafs into leafs if they have no children.
18284                 // it's not very rational behaviour..
18285                 
18286                 if(!this.wasLeaf && this.node.leaf){
18287                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
18288                     delete this.c1;
18289                     delete this.c2;
18290                     this.wasLeaf = true;
18291                 }
18292             }
18293             var ecc = "x-tree-ec-icon "+cls;
18294             if(this.ecc != ecc){
18295                 this.ecNode.className = ecc;
18296                 this.ecc = ecc;
18297             }
18298         }
18299     },
18300
18301     getChildIndent : function(){
18302         if(!this.childIndent){
18303             var buf = [];
18304             var p = this.node;
18305             while(p){
18306                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
18307                     if(!p.isLast()) {
18308                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
18309                     } else {
18310                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
18311                     }
18312                 }
18313                 p = p.parentNode;
18314             }
18315             this.childIndent = buf.join("");
18316         }
18317         return this.childIndent;
18318     },
18319
18320     renderIndent : function(){
18321         if(this.rendered){
18322             var indent = "";
18323             var p = this.node.parentNode;
18324             if(p){
18325                 indent = p.ui.getChildIndent();
18326             }
18327             if(this.indentMarkup != indent){ // don't rerender if not required
18328                 this.indentNode.innerHTML = indent;
18329                 this.indentMarkup = indent;
18330             }
18331             this.updateExpandIcon();
18332         }
18333     }
18334 };
18335
18336 Roo.tree.RootTreeNodeUI = function(){
18337     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
18338 };
18339 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
18340     render : function(){
18341         if(!this.rendered){
18342             var targetNode = this.node.ownerTree.innerCt.dom;
18343             this.node.expanded = true;
18344             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
18345             this.wrap = this.ctNode = targetNode.firstChild;
18346         }
18347     },
18348     collapse : function(){
18349     },
18350     expand : function(){
18351     }
18352 });/*
18353  * Based on:
18354  * Ext JS Library 1.1.1
18355  * Copyright(c) 2006-2007, Ext JS, LLC.
18356  *
18357  * Originally Released Under LGPL - original licence link has changed is not relivant.
18358  *
18359  * Fork - LGPL
18360  * <script type="text/javascript">
18361  */
18362 /**
18363  * @class Roo.tree.TreeLoader
18364  * @extends Roo.util.Observable
18365  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
18366  * nodes from a specified URL. The response must be a javascript Array definition
18367  * who's elements are node definition objects. eg:
18368  * <pre><code>
18369    [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
18370     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
18371 </code></pre>
18372  * <br><br>
18373  * A server request is sent, and child nodes are loaded only when a node is expanded.
18374  * The loading node's id is passed to the server under the parameter name "node" to
18375  * enable the server to produce the correct child nodes.
18376  * <br><br>
18377  * To pass extra parameters, an event handler may be attached to the "beforeload"
18378  * event, and the parameters specified in the TreeLoader's baseParams property:
18379  * <pre><code>
18380     myTreeLoader.on("beforeload", function(treeLoader, node) {
18381         this.baseParams.category = node.attributes.category;
18382     }, this);
18383 </code></pre><
18384  * This would pass an HTTP parameter called "category" to the server containing
18385  * the value of the Node's "category" attribute.
18386  * @constructor
18387  * Creates a new Treeloader.
18388  * @param {Object} config A config object containing config properties.
18389  */
18390 Roo.tree.TreeLoader = function(config){
18391     this.baseParams = {};
18392     this.requestMethod = "POST";
18393     Roo.apply(this, config);
18394
18395     this.addEvents({
18396     
18397         /**
18398          * @event beforeload
18399          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
18400          * @param {Object} This TreeLoader object.
18401          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18402          * @param {Object} callback The callback function specified in the {@link #load} call.
18403          */
18404         beforeload : true,
18405         /**
18406          * @event load
18407          * Fires when the node has been successfuly loaded.
18408          * @param {Object} This TreeLoader object.
18409          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18410          * @param {Object} response The response object containing the data from the server.
18411          */
18412         load : true,
18413         /**
18414          * @event loadexception
18415          * Fires if the network request failed.
18416          * @param {Object} This TreeLoader object.
18417          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18418          * @param {Object} response The response object containing the data from the server.
18419          */
18420         loadexception : true,
18421         /**
18422          * @event create
18423          * Fires before a node is created, enabling you to return custom Node types 
18424          * @param {Object} This TreeLoader object.
18425          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
18426          */
18427         create : true
18428     });
18429
18430     Roo.tree.TreeLoader.superclass.constructor.call(this);
18431 };
18432
18433 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
18434     /**
18435     * @cfg {String} dataUrl The URL from which to request a Json string which
18436     * specifies an array of node definition object representing the child nodes
18437     * to be loaded.
18438     */
18439     /**
18440     * @cfg {Object} baseParams (optional) An object containing properties which
18441     * specify HTTP parameters to be passed to each request for child nodes.
18442     */
18443     /**
18444     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
18445     * created by this loader. If the attributes sent by the server have an attribute in this object,
18446     * they take priority.
18447     */
18448     /**
18449     * @cfg {Object} uiProviders (optional) An object containing properties which
18450     * 
18451     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
18452     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
18453     * <i>uiProvider</i> attribute of a returned child node is a string rather
18454     * than a reference to a TreeNodeUI implementation, this that string value
18455     * is used as a property name in the uiProviders object. You can define the provider named
18456     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
18457     */
18458     uiProviders : {},
18459
18460     /**
18461     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
18462     * child nodes before loading.
18463     */
18464     clearOnLoad : true,
18465
18466     /**
18467     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
18468     * property on loading, rather than expecting an array. (eg. more compatible to a standard
18469     * Grid query { data : [ .....] }
18470     */
18471     
18472     root : false,
18473      /**
18474     * @cfg {String} queryParam (optional) 
18475     * Name of the query as it will be passed on the querystring (defaults to 'node')
18476     * eg. the request will be ?node=[id]
18477     */
18478     
18479     
18480     queryParam: false,
18481     
18482     /**
18483      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
18484      * This is called automatically when a node is expanded, but may be used to reload
18485      * a node (or append new children if the {@link #clearOnLoad} option is false.)
18486      * @param {Roo.tree.TreeNode} node
18487      * @param {Function} callback
18488      */
18489     load : function(node, callback){
18490         if(this.clearOnLoad){
18491             while(node.firstChild){
18492                 node.removeChild(node.firstChild);
18493             }
18494         }
18495         if(node.attributes.children){ // preloaded json children
18496             var cs = node.attributes.children;
18497             for(var i = 0, len = cs.length; i < len; i++){
18498                 node.appendChild(this.createNode(cs[i]));
18499             }
18500             if(typeof callback == "function"){
18501                 callback();
18502             }
18503         }else if(this.dataUrl){
18504             this.requestData(node, callback);
18505         }
18506     },
18507
18508     getParams: function(node){
18509         var buf = [], bp = this.baseParams;
18510         for(var key in bp){
18511             if(typeof bp[key] != "function"){
18512                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18513             }
18514         }
18515         var n = this.queryParam === false ? 'node' : this.queryParam;
18516         buf.push(n + "=", encodeURIComponent(node.id));
18517         return buf.join("");
18518     },
18519
18520     requestData : function(node, callback){
18521         if(this.fireEvent("beforeload", this, node, callback) !== false){
18522             this.transId = Roo.Ajax.request({
18523                 method:this.requestMethod,
18524                 url: this.dataUrl||this.url,
18525                 success: this.handleResponse,
18526                 failure: this.handleFailure,
18527                 scope: this,
18528                 argument: {callback: callback, node: node},
18529                 params: this.getParams(node)
18530             });
18531         }else{
18532             // if the load is cancelled, make sure we notify
18533             // the node that we are done
18534             if(typeof callback == "function"){
18535                 callback();
18536             }
18537         }
18538     },
18539
18540     isLoading : function(){
18541         return this.transId ? true : false;
18542     },
18543
18544     abort : function(){
18545         if(this.isLoading()){
18546             Roo.Ajax.abort(this.transId);
18547         }
18548     },
18549
18550     // private
18551     createNode : function(attr)
18552     {
18553         // apply baseAttrs, nice idea Corey!
18554         if(this.baseAttrs){
18555             Roo.applyIf(attr, this.baseAttrs);
18556         }
18557         if(this.applyLoader !== false){
18558             attr.loader = this;
18559         }
18560         // uiProvider = depreciated..
18561         
18562         if(typeof(attr.uiProvider) == 'string'){
18563            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
18564                 /**  eval:var:attr */ eval(attr.uiProvider);
18565         }
18566         if(typeof(this.uiProviders['default']) != 'undefined') {
18567             attr.uiProvider = this.uiProviders['default'];
18568         }
18569         
18570         this.fireEvent('create', this, attr);
18571         
18572         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18573         return(attr.leaf ?
18574                         new Roo.tree.TreeNode(attr) :
18575                         new Roo.tree.AsyncTreeNode(attr));
18576     },
18577
18578     processResponse : function(response, node, callback)
18579     {
18580         var json = response.responseText;
18581         try {
18582             
18583             var o = Roo.decode(json);
18584             
18585             if (!o.success) {
18586                 // it's a failure condition.
18587                 var a = response.argument;
18588                 this.fireEvent("loadexception", this, a.node, response);
18589                 Roo.log("Load failed - should have a handler really");
18590                 return;
18591             }
18592             
18593             if (this.root !== false) {
18594                 o = o[this.root];
18595             }
18596             
18597             for(var i = 0, len = o.length; i < len; i++){
18598                 var n = this.createNode(o[i]);
18599                 if(n){
18600                     node.appendChild(n);
18601                 }
18602             }
18603             if(typeof callback == "function"){
18604                 callback(this, node);
18605             }
18606         }catch(e){
18607             this.handleFailure(response);
18608         }
18609     },
18610
18611     handleResponse : function(response){
18612         this.transId = false;
18613         var a = response.argument;
18614         this.processResponse(response, a.node, a.callback);
18615         this.fireEvent("load", this, a.node, response);
18616     },
18617
18618     handleFailure : function(response)
18619     {
18620         // should handle failure better..
18621         this.transId = false;
18622         var a = response.argument;
18623         this.fireEvent("loadexception", this, a.node, response);
18624         if(typeof a.callback == "function"){
18625             a.callback(this, a.node);
18626         }
18627     }
18628 });/*
18629  * Based on:
18630  * Ext JS Library 1.1.1
18631  * Copyright(c) 2006-2007, Ext JS, LLC.
18632  *
18633  * Originally Released Under LGPL - original licence link has changed is not relivant.
18634  *
18635  * Fork - LGPL
18636  * <script type="text/javascript">
18637  */
18638
18639 /**
18640 * @class Roo.tree.TreeFilter
18641 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18642 * @param {TreePanel} tree
18643 * @param {Object} config (optional)
18644  */
18645 Roo.tree.TreeFilter = function(tree, config){
18646     this.tree = tree;
18647     this.filtered = {};
18648     Roo.apply(this, config);
18649 };
18650
18651 Roo.tree.TreeFilter.prototype = {
18652     clearBlank:false,
18653     reverse:false,
18654     autoClear:false,
18655     remove:false,
18656
18657      /**
18658      * Filter the data by a specific attribute.
18659      * @param {String/RegExp} value Either string that the attribute value
18660      * should start with or a RegExp to test against the attribute
18661      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18662      * @param {TreeNode} startNode (optional) The node to start the filter at.
18663      */
18664     filter : function(value, attr, startNode){
18665         attr = attr || "text";
18666         var f;
18667         if(typeof value == "string"){
18668             var vlen = value.length;
18669             // auto clear empty filter
18670             if(vlen == 0 && this.clearBlank){
18671                 this.clear();
18672                 return;
18673             }
18674             value = value.toLowerCase();
18675             f = function(n){
18676                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18677             };
18678         }else if(value.exec){ // regex?
18679             f = function(n){
18680                 return value.test(n.attributes[attr]);
18681             };
18682         }else{
18683             throw 'Illegal filter type, must be string or regex';
18684         }
18685         this.filterBy(f, null, startNode);
18686         },
18687
18688     /**
18689      * Filter by a function. The passed function will be called with each
18690      * node in the tree (or from the startNode). If the function returns true, the node is kept
18691      * otherwise it is filtered. If a node is filtered, its children are also filtered.
18692      * @param {Function} fn The filter function
18693      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18694      */
18695     filterBy : function(fn, scope, startNode){
18696         startNode = startNode || this.tree.root;
18697         if(this.autoClear){
18698             this.clear();
18699         }
18700         var af = this.filtered, rv = this.reverse;
18701         var f = function(n){
18702             if(n == startNode){
18703                 return true;
18704             }
18705             if(af[n.id]){
18706                 return false;
18707             }
18708             var m = fn.call(scope || n, n);
18709             if(!m || rv){
18710                 af[n.id] = n;
18711                 n.ui.hide();
18712                 return false;
18713             }
18714             return true;
18715         };
18716         startNode.cascade(f);
18717         if(this.remove){
18718            for(var id in af){
18719                if(typeof id != "function"){
18720                    var n = af[id];
18721                    if(n && n.parentNode){
18722                        n.parentNode.removeChild(n);
18723                    }
18724                }
18725            }
18726         }
18727     },
18728
18729     /**
18730      * Clears the current filter. Note: with the "remove" option
18731      * set a filter cannot be cleared.
18732      */
18733     clear : function(){
18734         var t = this.tree;
18735         var af = this.filtered;
18736         for(var id in af){
18737             if(typeof id != "function"){
18738                 var n = af[id];
18739                 if(n){
18740                     n.ui.show();
18741                 }
18742             }
18743         }
18744         this.filtered = {};
18745     }
18746 };
18747 /*
18748  * Based on:
18749  * Ext JS Library 1.1.1
18750  * Copyright(c) 2006-2007, Ext JS, LLC.
18751  *
18752  * Originally Released Under LGPL - original licence link has changed is not relivant.
18753  *
18754  * Fork - LGPL
18755  * <script type="text/javascript">
18756  */
18757  
18758
18759 /**
18760  * @class Roo.tree.TreeSorter
18761  * Provides sorting of nodes in a TreePanel
18762  * 
18763  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18764  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18765  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18766  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18767  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18768  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18769  * @constructor
18770  * @param {TreePanel} tree
18771  * @param {Object} config
18772  */
18773 Roo.tree.TreeSorter = function(tree, config){
18774     Roo.apply(this, config);
18775     tree.on("beforechildrenrendered", this.doSort, this);
18776     tree.on("append", this.updateSort, this);
18777     tree.on("insert", this.updateSort, this);
18778     
18779     var dsc = this.dir && this.dir.toLowerCase() == "desc";
18780     var p = this.property || "text";
18781     var sortType = this.sortType;
18782     var fs = this.folderSort;
18783     var cs = this.caseSensitive === true;
18784     var leafAttr = this.leafAttr || 'leaf';
18785
18786     this.sortFn = function(n1, n2){
18787         if(fs){
18788             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18789                 return 1;
18790             }
18791             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
18792                 return -1;
18793             }
18794         }
18795         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
18796         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
18797         if(v1 < v2){
18798                         return dsc ? +1 : -1;
18799                 }else if(v1 > v2){
18800                         return dsc ? -1 : +1;
18801         }else{
18802                 return 0;
18803         }
18804     };
18805 };
18806
18807 Roo.tree.TreeSorter.prototype = {
18808     doSort : function(node){
18809         node.sort(this.sortFn);
18810     },
18811     
18812     compareNodes : function(n1, n2){
18813         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
18814     },
18815     
18816     updateSort : function(tree, node){
18817         if(node.childrenRendered){
18818             this.doSort.defer(1, this, [node]);
18819         }
18820     }
18821 };/*
18822  * Based on:
18823  * Ext JS Library 1.1.1
18824  * Copyright(c) 2006-2007, Ext JS, LLC.
18825  *
18826  * Originally Released Under LGPL - original licence link has changed is not relivant.
18827  *
18828  * Fork - LGPL
18829  * <script type="text/javascript">
18830  */
18831
18832 if(Roo.dd.DropZone){
18833     
18834 Roo.tree.TreeDropZone = function(tree, config){
18835     this.allowParentInsert = false;
18836     this.allowContainerDrop = false;
18837     this.appendOnly = false;
18838     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
18839     this.tree = tree;
18840     this.lastInsertClass = "x-tree-no-status";
18841     this.dragOverData = {};
18842 };
18843
18844 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
18845     ddGroup : "TreeDD",
18846     
18847     expandDelay : 1000,
18848     
18849     expandNode : function(node){
18850         if(node.hasChildNodes() && !node.isExpanded()){
18851             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
18852         }
18853     },
18854     
18855     queueExpand : function(node){
18856         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
18857     },
18858     
18859     cancelExpand : function(){
18860         if(this.expandProcId){
18861             clearTimeout(this.expandProcId);
18862             this.expandProcId = false;
18863         }
18864     },
18865     
18866     isValidDropPoint : function(n, pt, dd, e, data){
18867         if(!n || !data){ return false; }
18868         var targetNode = n.node;
18869         var dropNode = data.node;
18870         // default drop rules
18871         if(!(targetNode && targetNode.isTarget && pt)){
18872             return false;
18873         }
18874         if(pt == "append" && targetNode.allowChildren === false){
18875             return false;
18876         }
18877         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
18878             return false;
18879         }
18880         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
18881             return false;
18882         }
18883         // reuse the object
18884         var overEvent = this.dragOverData;
18885         overEvent.tree = this.tree;
18886         overEvent.target = targetNode;
18887         overEvent.data = data;
18888         overEvent.point = pt;
18889         overEvent.source = dd;
18890         overEvent.rawEvent = e;
18891         overEvent.dropNode = dropNode;
18892         overEvent.cancel = false;  
18893         var result = this.tree.fireEvent("nodedragover", overEvent);
18894         return overEvent.cancel === false && result !== false;
18895     },
18896     
18897     getDropPoint : function(e, n, dd){
18898         var tn = n.node;
18899         if(tn.isRoot){
18900             return tn.allowChildren !== false ? "append" : false; // always append for root
18901         }
18902         var dragEl = n.ddel;
18903         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
18904         var y = Roo.lib.Event.getPageY(e);
18905         //var noAppend = tn.allowChildren === false || tn.isLeaf();
18906         
18907         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
18908         var noAppend = tn.allowChildren === false;
18909         if(this.appendOnly || tn.parentNode.allowChildren === false){
18910             return noAppend ? false : "append";
18911         }
18912         var noBelow = false;
18913         if(!this.allowParentInsert){
18914             noBelow = tn.hasChildNodes() && tn.isExpanded();
18915         }
18916         var q = (b - t) / (noAppend ? 2 : 3);
18917         if(y >= t && y < (t + q)){
18918             return "above";
18919         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
18920             return "below";
18921         }else{
18922             return "append";
18923         }
18924     },
18925     
18926     onNodeEnter : function(n, dd, e, data){
18927         this.cancelExpand();
18928     },
18929     
18930     onNodeOver : function(n, dd, e, data){
18931         var pt = this.getDropPoint(e, n, dd);
18932         var node = n.node;
18933         
18934         // auto node expand check
18935         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
18936             this.queueExpand(node);
18937         }else if(pt != "append"){
18938             this.cancelExpand();
18939         }
18940         
18941         // set the insert point style on the target node
18942         var returnCls = this.dropNotAllowed;
18943         if(this.isValidDropPoint(n, pt, dd, e, data)){
18944            if(pt){
18945                var el = n.ddel;
18946                var cls;
18947                if(pt == "above"){
18948                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
18949                    cls = "x-tree-drag-insert-above";
18950                }else if(pt == "below"){
18951                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
18952                    cls = "x-tree-drag-insert-below";
18953                }else{
18954                    returnCls = "x-tree-drop-ok-append";
18955                    cls = "x-tree-drag-append";
18956                }
18957                if(this.lastInsertClass != cls){
18958                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
18959                    this.lastInsertClass = cls;
18960                }
18961            }
18962        }
18963        return returnCls;
18964     },
18965     
18966     onNodeOut : function(n, dd, e, data){
18967         this.cancelExpand();
18968         this.removeDropIndicators(n);
18969     },
18970     
18971     onNodeDrop : function(n, dd, e, data){
18972         var point = this.getDropPoint(e, n, dd);
18973         var targetNode = n.node;
18974         targetNode.ui.startDrop();
18975         if(!this.isValidDropPoint(n, point, dd, e, data)){
18976             targetNode.ui.endDrop();
18977             return false;
18978         }
18979         // first try to find the drop node
18980         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
18981         var dropEvent = {
18982             tree : this.tree,
18983             target: targetNode,
18984             data: data,
18985             point: point,
18986             source: dd,
18987             rawEvent: e,
18988             dropNode: dropNode,
18989             cancel: !dropNode   
18990         };
18991         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
18992         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
18993             targetNode.ui.endDrop();
18994             return false;
18995         }
18996         // allow target changing
18997         targetNode = dropEvent.target;
18998         if(point == "append" && !targetNode.isExpanded()){
18999             targetNode.expand(false, null, function(){
19000                 this.completeDrop(dropEvent);
19001             }.createDelegate(this));
19002         }else{
19003             this.completeDrop(dropEvent);
19004         }
19005         return true;
19006     },
19007     
19008     completeDrop : function(de){
19009         var ns = de.dropNode, p = de.point, t = de.target;
19010         if(!(ns instanceof Array)){
19011             ns = [ns];
19012         }
19013         var n;
19014         for(var i = 0, len = ns.length; i < len; i++){
19015             n = ns[i];
19016             if(p == "above"){
19017                 t.parentNode.insertBefore(n, t);
19018             }else if(p == "below"){
19019                 t.parentNode.insertBefore(n, t.nextSibling);
19020             }else{
19021                 t.appendChild(n);
19022             }
19023         }
19024         n.ui.focus();
19025         if(this.tree.hlDrop){
19026             n.ui.highlight();
19027         }
19028         t.ui.endDrop();
19029         this.tree.fireEvent("nodedrop", de);
19030     },
19031     
19032     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
19033         if(this.tree.hlDrop){
19034             dropNode.ui.focus();
19035             dropNode.ui.highlight();
19036         }
19037         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
19038     },
19039     
19040     getTree : function(){
19041         return this.tree;
19042     },
19043     
19044     removeDropIndicators : function(n){
19045         if(n && n.ddel){
19046             var el = n.ddel;
19047             Roo.fly(el).removeClass([
19048                     "x-tree-drag-insert-above",
19049                     "x-tree-drag-insert-below",
19050                     "x-tree-drag-append"]);
19051             this.lastInsertClass = "_noclass";
19052         }
19053     },
19054     
19055     beforeDragDrop : function(target, e, id){
19056         this.cancelExpand();
19057         return true;
19058     },
19059     
19060     afterRepair : function(data){
19061         if(data && Roo.enableFx){
19062             data.node.ui.highlight();
19063         }
19064         this.hideProxy();
19065     }    
19066 });
19067
19068 }
19069 /*
19070  * Based on:
19071  * Ext JS Library 1.1.1
19072  * Copyright(c) 2006-2007, Ext JS, LLC.
19073  *
19074  * Originally Released Under LGPL - original licence link has changed is not relivant.
19075  *
19076  * Fork - LGPL
19077  * <script type="text/javascript">
19078  */
19079  
19080
19081 if(Roo.dd.DragZone){
19082 Roo.tree.TreeDragZone = function(tree, config){
19083     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
19084     this.tree = tree;
19085 };
19086
19087 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
19088     ddGroup : "TreeDD",
19089     
19090     onBeforeDrag : function(data, e){
19091         var n = data.node;
19092         return n && n.draggable && !n.disabled;
19093     },
19094     
19095     onInitDrag : function(e){
19096         var data = this.dragData;
19097         this.tree.getSelectionModel().select(data.node);
19098         this.proxy.update("");
19099         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
19100         this.tree.fireEvent("startdrag", this.tree, data.node, e);
19101     },
19102     
19103     getRepairXY : function(e, data){
19104         return data.node.ui.getDDRepairXY();
19105     },
19106     
19107     onEndDrag : function(data, e){
19108         this.tree.fireEvent("enddrag", this.tree, data.node, e);
19109     },
19110     
19111     onValidDrop : function(dd, e, id){
19112         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
19113         this.hideProxy();
19114     },
19115     
19116     beforeInvalidDrop : function(e, id){
19117         // this scrolls the original position back into view
19118         var sm = this.tree.getSelectionModel();
19119         sm.clearSelections();
19120         sm.select(this.dragData.node);
19121     }
19122 });
19123 }/*
19124  * Based on:
19125  * Ext JS Library 1.1.1
19126  * Copyright(c) 2006-2007, Ext JS, LLC.
19127  *
19128  * Originally Released Under LGPL - original licence link has changed is not relivant.
19129  *
19130  * Fork - LGPL
19131  * <script type="text/javascript">
19132  */
19133 /**
19134  * @class Roo.tree.TreeEditor
19135  * @extends Roo.Editor
19136  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
19137  * as the editor field.
19138  * @constructor
19139  * @param {Object} config (used to be the tree panel.)
19140  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
19141  * 
19142  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
19143  * @cfg {Roo.form.TextField|Object} field The field configuration
19144  *
19145  * 
19146  */
19147 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
19148     var tree = config;
19149     var field;
19150     if (oldconfig) { // old style..
19151         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
19152     } else {
19153         // new style..
19154         tree = config.tree;
19155         config.field = config.field  || {};
19156         config.field.xtype = 'TextField';
19157         field = Roo.factory(config.field, Roo.form);
19158     }
19159     config = config || {};
19160     
19161     
19162     this.addEvents({
19163         /**
19164          * @event beforenodeedit
19165          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
19166          * false from the handler of this event.
19167          * @param {Editor} this
19168          * @param {Roo.tree.Node} node 
19169          */
19170         "beforenodeedit" : true
19171     });
19172     
19173     //Roo.log(config);
19174     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
19175
19176     this.tree = tree;
19177
19178     tree.on('beforeclick', this.beforeNodeClick, this);
19179     tree.getTreeEl().on('mousedown', this.hide, this);
19180     this.on('complete', this.updateNode, this);
19181     this.on('beforestartedit', this.fitToTree, this);
19182     this.on('startedit', this.bindScroll, this, {delay:10});
19183     this.on('specialkey', this.onSpecialKey, this);
19184 };
19185
19186 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
19187     /**
19188      * @cfg {String} alignment
19189      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
19190      */
19191     alignment: "l-l",
19192     // inherit
19193     autoSize: false,
19194     /**
19195      * @cfg {Boolean} hideEl
19196      * True to hide the bound element while the editor is displayed (defaults to false)
19197      */
19198     hideEl : false,
19199     /**
19200      * @cfg {String} cls
19201      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
19202      */
19203     cls: "x-small-editor x-tree-editor",
19204     /**
19205      * @cfg {Boolean} shim
19206      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
19207      */
19208     shim:false,
19209     // inherit
19210     shadow:"frame",
19211     /**
19212      * @cfg {Number} maxWidth
19213      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
19214      * the containing tree element's size, it will be automatically limited for you to the container width, taking
19215      * scroll and client offsets into account prior to each edit.
19216      */
19217     maxWidth: 250,
19218
19219     editDelay : 350,
19220
19221     // private
19222     fitToTree : function(ed, el){
19223         var td = this.tree.getTreeEl().dom, nd = el.dom;
19224         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
19225             td.scrollLeft = nd.offsetLeft;
19226         }
19227         var w = Math.min(
19228                 this.maxWidth,
19229                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
19230         this.setSize(w, '');
19231         
19232         return this.fireEvent('beforenodeedit', this, this.editNode);
19233         
19234     },
19235
19236     // private
19237     triggerEdit : function(node){
19238         this.completeEdit();
19239         this.editNode = node;
19240         this.startEdit(node.ui.textNode, node.text);
19241     },
19242
19243     // private
19244     bindScroll : function(){
19245         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
19246     },
19247
19248     // private
19249     beforeNodeClick : function(node, e){
19250         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
19251         this.lastClick = new Date();
19252         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
19253             e.stopEvent();
19254             this.triggerEdit(node);
19255             return false;
19256         }
19257         return true;
19258     },
19259
19260     // private
19261     updateNode : function(ed, value){
19262         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
19263         this.editNode.setText(value);
19264     },
19265
19266     // private
19267     onHide : function(){
19268         Roo.tree.TreeEditor.superclass.onHide.call(this);
19269         if(this.editNode){
19270             this.editNode.ui.focus();
19271         }
19272     },
19273
19274     // private
19275     onSpecialKey : function(field, e){
19276         var k = e.getKey();
19277         if(k == e.ESC){
19278             e.stopEvent();
19279             this.cancelEdit();
19280         }else if(k == e.ENTER && !e.hasModifier()){
19281             e.stopEvent();
19282             this.completeEdit();
19283         }
19284     }
19285 });//<Script type="text/javascript">
19286 /*
19287  * Based on:
19288  * Ext JS Library 1.1.1
19289  * Copyright(c) 2006-2007, Ext JS, LLC.
19290  *
19291  * Originally Released Under LGPL - original licence link has changed is not relivant.
19292  *
19293  * Fork - LGPL
19294  * <script type="text/javascript">
19295  */
19296  
19297 /**
19298  * Not documented??? - probably should be...
19299  */
19300
19301 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
19302     //focus: Roo.emptyFn, // prevent odd scrolling behavior
19303     
19304     renderElements : function(n, a, targetNode, bulkRender){
19305         //consel.log("renderElements?");
19306         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
19307
19308         var t = n.getOwnerTree();
19309         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
19310         
19311         var cols = t.columns;
19312         var bw = t.borderWidth;
19313         var c = cols[0];
19314         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
19315          var cb = typeof a.checked == "boolean";
19316         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19317         var colcls = 'x-t-' + tid + '-c0';
19318         var buf = [
19319             '<li class="x-tree-node">',
19320             
19321                 
19322                 '<div class="x-tree-node-el ', a.cls,'">',
19323                     // extran...
19324                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
19325                 
19326                 
19327                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
19328                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
19329                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
19330                            (a.icon ? ' x-tree-node-inline-icon' : ''),
19331                            (a.iconCls ? ' '+a.iconCls : ''),
19332                            '" unselectable="on" />',
19333                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
19334                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
19335                              
19336                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19337                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
19338                             '<span unselectable="on" qtip="' + tx + '">',
19339                              tx,
19340                              '</span></a>' ,
19341                     '</div>',
19342                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19343                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
19344                  ];
19345         for(var i = 1, len = cols.length; i < len; i++){
19346             c = cols[i];
19347             colcls = 'x-t-' + tid + '-c' +i;
19348             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19349             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
19350                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
19351                       "</div>");
19352          }
19353          
19354          buf.push(
19355             '</a>',
19356             '<div class="x-clear"></div></div>',
19357             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
19358             "</li>");
19359         
19360         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
19361             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
19362                                 n.nextSibling.ui.getEl(), buf.join(""));
19363         }else{
19364             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
19365         }
19366         var el = this.wrap.firstChild;
19367         this.elRow = el;
19368         this.elNode = el.firstChild;
19369         this.ranchor = el.childNodes[1];
19370         this.ctNode = this.wrap.childNodes[1];
19371         var cs = el.firstChild.childNodes;
19372         this.indentNode = cs[0];
19373         this.ecNode = cs[1];
19374         this.iconNode = cs[2];
19375         var index = 3;
19376         if(cb){
19377             this.checkbox = cs[3];
19378             index++;
19379         }
19380         this.anchor = cs[index];
19381         
19382         this.textNode = cs[index].firstChild;
19383         
19384         //el.on("click", this.onClick, this);
19385         //el.on("dblclick", this.onDblClick, this);
19386         
19387         
19388        // console.log(this);
19389     },
19390     initEvents : function(){
19391         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
19392         
19393             
19394         var a = this.ranchor;
19395
19396         var el = Roo.get(a);
19397
19398         if(Roo.isOpera){ // opera render bug ignores the CSS
19399             el.setStyle("text-decoration", "none");
19400         }
19401
19402         el.on("click", this.onClick, this);
19403         el.on("dblclick", this.onDblClick, this);
19404         el.on("contextmenu", this.onContextMenu, this);
19405         
19406     },
19407     
19408     /*onSelectedChange : function(state){
19409         if(state){
19410             this.focus();
19411             this.addClass("x-tree-selected");
19412         }else{
19413             //this.blur();
19414             this.removeClass("x-tree-selected");
19415         }
19416     },*/
19417     addClass : function(cls){
19418         if(this.elRow){
19419             Roo.fly(this.elRow).addClass(cls);
19420         }
19421         
19422     },
19423     
19424     
19425     removeClass : function(cls){
19426         if(this.elRow){
19427             Roo.fly(this.elRow).removeClass(cls);
19428         }
19429     }
19430
19431     
19432     
19433 });//<Script type="text/javascript">
19434
19435 /*
19436  * Based on:
19437  * Ext JS Library 1.1.1
19438  * Copyright(c) 2006-2007, Ext JS, LLC.
19439  *
19440  * Originally Released Under LGPL - original licence link has changed is not relivant.
19441  *
19442  * Fork - LGPL
19443  * <script type="text/javascript">
19444  */
19445  
19446
19447 /**
19448  * @class Roo.tree.ColumnTree
19449  * @extends Roo.data.TreePanel
19450  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
19451  * @cfg {int} borderWidth  compined right/left border allowance
19452  * @constructor
19453  * @param {String/HTMLElement/Element} el The container element
19454  * @param {Object} config
19455  */
19456 Roo.tree.ColumnTree =  function(el, config)
19457 {
19458    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
19459    this.addEvents({
19460         /**
19461         * @event resize
19462         * Fire this event on a container when it resizes
19463         * @param {int} w Width
19464         * @param {int} h Height
19465         */
19466        "resize" : true
19467     });
19468     this.on('resize', this.onResize, this);
19469 };
19470
19471 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
19472     //lines:false,
19473     
19474     
19475     borderWidth: Roo.isBorderBox ? 0 : 2, 
19476     headEls : false,
19477     
19478     render : function(){
19479         // add the header.....
19480        
19481         Roo.tree.ColumnTree.superclass.render.apply(this);
19482         
19483         this.el.addClass('x-column-tree');
19484         
19485         this.headers = this.el.createChild(
19486             {cls:'x-tree-headers'},this.innerCt.dom);
19487    
19488         var cols = this.columns, c;
19489         var totalWidth = 0;
19490         this.headEls = [];
19491         var  len = cols.length;
19492         for(var i = 0; i < len; i++){
19493              c = cols[i];
19494              totalWidth += c.width;
19495             this.headEls.push(this.headers.createChild({
19496                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19497                  cn: {
19498                      cls:'x-tree-hd-text',
19499                      html: c.header
19500                  },
19501                  style:'width:'+(c.width-this.borderWidth)+'px;'
19502              }));
19503         }
19504         this.headers.createChild({cls:'x-clear'});
19505         // prevent floats from wrapping when clipped
19506         this.headers.setWidth(totalWidth);
19507         //this.innerCt.setWidth(totalWidth);
19508         this.innerCt.setStyle({ overflow: 'auto' });
19509         this.onResize(this.width, this.height);
19510              
19511         
19512     },
19513     onResize : function(w,h)
19514     {
19515         this.height = h;
19516         this.width = w;
19517         // resize cols..
19518         this.innerCt.setWidth(this.width);
19519         this.innerCt.setHeight(this.height-20);
19520         
19521         // headers...
19522         var cols = this.columns, c;
19523         var totalWidth = 0;
19524         var expEl = false;
19525         var len = cols.length;
19526         for(var i = 0; i < len; i++){
19527             c = cols[i];
19528             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19529                 // it's the expander..
19530                 expEl  = this.headEls[i];
19531                 continue;
19532             }
19533             totalWidth += c.width;
19534             
19535         }
19536         if (expEl) {
19537             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
19538         }
19539         this.headers.setWidth(w-20);
19540
19541         
19542         
19543         
19544     }
19545 });
19546 /*
19547  * Based on:
19548  * Ext JS Library 1.1.1
19549  * Copyright(c) 2006-2007, Ext JS, LLC.
19550  *
19551  * Originally Released Under LGPL - original licence link has changed is not relivant.
19552  *
19553  * Fork - LGPL
19554  * <script type="text/javascript">
19555  */
19556  
19557 /**
19558  * @class Roo.menu.Menu
19559  * @extends Roo.util.Observable
19560  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
19561  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19562  * @constructor
19563  * Creates a new Menu
19564  * @param {Object} config Configuration options
19565  */
19566 Roo.menu.Menu = function(config){
19567     Roo.apply(this, config);
19568     this.id = this.id || Roo.id();
19569     this.addEvents({
19570         /**
19571          * @event beforeshow
19572          * Fires before this menu is displayed
19573          * @param {Roo.menu.Menu} this
19574          */
19575         beforeshow : true,
19576         /**
19577          * @event beforehide
19578          * Fires before this menu is hidden
19579          * @param {Roo.menu.Menu} this
19580          */
19581         beforehide : true,
19582         /**
19583          * @event show
19584          * Fires after this menu is displayed
19585          * @param {Roo.menu.Menu} this
19586          */
19587         show : true,
19588         /**
19589          * @event hide
19590          * Fires after this menu is hidden
19591          * @param {Roo.menu.Menu} this
19592          */
19593         hide : true,
19594         /**
19595          * @event click
19596          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19597          * @param {Roo.menu.Menu} this
19598          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19599          * @param {Roo.EventObject} e
19600          */
19601         click : true,
19602         /**
19603          * @event mouseover
19604          * Fires when the mouse is hovering over this menu
19605          * @param {Roo.menu.Menu} this
19606          * @param {Roo.EventObject} e
19607          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19608          */
19609         mouseover : true,
19610         /**
19611          * @event mouseout
19612          * Fires when the mouse exits this menu
19613          * @param {Roo.menu.Menu} this
19614          * @param {Roo.EventObject} e
19615          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19616          */
19617         mouseout : true,
19618         /**
19619          * @event itemclick
19620          * Fires when a menu item contained in this menu is clicked
19621          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19622          * @param {Roo.EventObject} e
19623          */
19624         itemclick: true
19625     });
19626     if (this.registerMenu) {
19627         Roo.menu.MenuMgr.register(this);
19628     }
19629     
19630     var mis = this.items;
19631     this.items = new Roo.util.MixedCollection();
19632     if(mis){
19633         this.add.apply(this, mis);
19634     }
19635 };
19636
19637 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19638     /**
19639      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19640      */
19641     minWidth : 120,
19642     /**
19643      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19644      * for bottom-right shadow (defaults to "sides")
19645      */
19646     shadow : "sides",
19647     /**
19648      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19649      * this menu (defaults to "tl-tr?")
19650      */
19651     subMenuAlign : "tl-tr?",
19652     /**
19653      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19654      * relative to its element of origin (defaults to "tl-bl?")
19655      */
19656     defaultAlign : "tl-bl?",
19657     /**
19658      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19659      */
19660     allowOtherMenus : false,
19661     /**
19662      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19663      */
19664     registerMenu : true,
19665
19666     hidden:true,
19667
19668     // private
19669     render : function(){
19670         if(this.el){
19671             return;
19672         }
19673         var el = this.el = new Roo.Layer({
19674             cls: "x-menu",
19675             shadow:this.shadow,
19676             constrain: false,
19677             parentEl: this.parentEl || document.body,
19678             zindex:15000
19679         });
19680
19681         this.keyNav = new Roo.menu.MenuNav(this);
19682
19683         if(this.plain){
19684             el.addClass("x-menu-plain");
19685         }
19686         if(this.cls){
19687             el.addClass(this.cls);
19688         }
19689         // generic focus element
19690         this.focusEl = el.createChild({
19691             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19692         });
19693         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19694         ul.on("click", this.onClick, this);
19695         ul.on("mouseover", this.onMouseOver, this);
19696         ul.on("mouseout", this.onMouseOut, this);
19697         this.items.each(function(item){
19698             var li = document.createElement("li");
19699             li.className = "x-menu-list-item";
19700             ul.dom.appendChild(li);
19701             item.render(li, this);
19702         }, this);
19703         this.ul = ul;
19704         this.autoWidth();
19705     },
19706
19707     // private
19708     autoWidth : function(){
19709         var el = this.el, ul = this.ul;
19710         if(!el){
19711             return;
19712         }
19713         var w = this.width;
19714         if(w){
19715             el.setWidth(w);
19716         }else if(Roo.isIE){
19717             el.setWidth(this.minWidth);
19718             var t = el.dom.offsetWidth; // force recalc
19719             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19720         }
19721     },
19722
19723     // private
19724     delayAutoWidth : function(){
19725         if(this.rendered){
19726             if(!this.awTask){
19727                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19728             }
19729             this.awTask.delay(20);
19730         }
19731     },
19732
19733     // private
19734     findTargetItem : function(e){
19735         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
19736         if(t && t.menuItemId){
19737             return this.items.get(t.menuItemId);
19738         }
19739     },
19740
19741     // private
19742     onClick : function(e){
19743         var t;
19744         if(t = this.findTargetItem(e)){
19745             t.onClick(e);
19746             this.fireEvent("click", this, t, e);
19747         }
19748     },
19749
19750     // private
19751     setActiveItem : function(item, autoExpand){
19752         if(item != this.activeItem){
19753             if(this.activeItem){
19754                 this.activeItem.deactivate();
19755             }
19756             this.activeItem = item;
19757             item.activate(autoExpand);
19758         }else if(autoExpand){
19759             item.expandMenu();
19760         }
19761     },
19762
19763     // private
19764     tryActivate : function(start, step){
19765         var items = this.items;
19766         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19767             var item = items.get(i);
19768             if(!item.disabled && item.canActivate){
19769                 this.setActiveItem(item, false);
19770                 return item;
19771             }
19772         }
19773         return false;
19774     },
19775
19776     // private
19777     onMouseOver : function(e){
19778         var t;
19779         if(t = this.findTargetItem(e)){
19780             if(t.canActivate && !t.disabled){
19781                 this.setActiveItem(t, true);
19782             }
19783         }
19784         this.fireEvent("mouseover", this, e, t);
19785     },
19786
19787     // private
19788     onMouseOut : function(e){
19789         var t;
19790         if(t = this.findTargetItem(e)){
19791             if(t == this.activeItem && t.shouldDeactivate(e)){
19792                 this.activeItem.deactivate();
19793                 delete this.activeItem;
19794             }
19795         }
19796         this.fireEvent("mouseout", this, e, t);
19797     },
19798
19799     /**
19800      * Read-only.  Returns true if the menu is currently displayed, else false.
19801      * @type Boolean
19802      */
19803     isVisible : function(){
19804         return this.el && !this.hidden;
19805     },
19806
19807     /**
19808      * Displays this menu relative to another element
19809      * @param {String/HTMLElement/Roo.Element} element The element to align to
19810      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
19811      * the element (defaults to this.defaultAlign)
19812      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19813      */
19814     show : function(el, pos, parentMenu){
19815         this.parentMenu = parentMenu;
19816         if(!this.el){
19817             this.render();
19818         }
19819         this.fireEvent("beforeshow", this);
19820         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
19821     },
19822
19823     /**
19824      * Displays this menu at a specific xy position
19825      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
19826      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19827      */
19828     showAt : function(xy, parentMenu, /* private: */_e){
19829         this.parentMenu = parentMenu;
19830         if(!this.el){
19831             this.render();
19832         }
19833         if(_e !== false){
19834             this.fireEvent("beforeshow", this);
19835             xy = this.el.adjustForConstraints(xy);
19836         }
19837         this.el.setXY(xy);
19838         this.el.show();
19839         this.hidden = false;
19840         this.focus();
19841         this.fireEvent("show", this);
19842     },
19843
19844     focus : function(){
19845         if(!this.hidden){
19846             this.doFocus.defer(50, this);
19847         }
19848     },
19849
19850     doFocus : function(){
19851         if(!this.hidden){
19852             this.focusEl.focus();
19853         }
19854     },
19855
19856     /**
19857      * Hides this menu and optionally all parent menus
19858      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
19859      */
19860     hide : function(deep){
19861         if(this.el && this.isVisible()){
19862             this.fireEvent("beforehide", this);
19863             if(this.activeItem){
19864                 this.activeItem.deactivate();
19865                 this.activeItem = null;
19866             }
19867             this.el.hide();
19868             this.hidden = true;
19869             this.fireEvent("hide", this);
19870         }
19871         if(deep === true && this.parentMenu){
19872             this.parentMenu.hide(true);
19873         }
19874     },
19875
19876     /**
19877      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
19878      * Any of the following are valid:
19879      * <ul>
19880      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
19881      * <li>An HTMLElement object which will be converted to a menu item</li>
19882      * <li>A menu item config object that will be created as a new menu item</li>
19883      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
19884      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
19885      * </ul>
19886      * Usage:
19887      * <pre><code>
19888 // Create the menu
19889 var menu = new Roo.menu.Menu();
19890
19891 // Create a menu item to add by reference
19892 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
19893
19894 // Add a bunch of items at once using different methods.
19895 // Only the last item added will be returned.
19896 var item = menu.add(
19897     menuItem,                // add existing item by ref
19898     'Dynamic Item',          // new TextItem
19899     '-',                     // new separator
19900     { text: 'Config Item' }  // new item by config
19901 );
19902 </code></pre>
19903      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
19904      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
19905      */
19906     add : function(){
19907         var a = arguments, l = a.length, item;
19908         for(var i = 0; i < l; i++){
19909             var el = a[i];
19910             if ((typeof(el) == "object") && el.xtype && el.xns) {
19911                 el = Roo.factory(el, Roo.menu);
19912             }
19913             
19914             if(el.render){ // some kind of Item
19915                 item = this.addItem(el);
19916             }else if(typeof el == "string"){ // string
19917                 if(el == "separator" || el == "-"){
19918                     item = this.addSeparator();
19919                 }else{
19920                     item = this.addText(el);
19921                 }
19922             }else if(el.tagName || el.el){ // element
19923                 item = this.addElement(el);
19924             }else if(typeof el == "object"){ // must be menu item config?
19925                 item = this.addMenuItem(el);
19926             }
19927         }
19928         return item;
19929     },
19930
19931     /**
19932      * Returns this menu's underlying {@link Roo.Element} object
19933      * @return {Roo.Element} The element
19934      */
19935     getEl : function(){
19936         if(!this.el){
19937             this.render();
19938         }
19939         return this.el;
19940     },
19941
19942     /**
19943      * Adds a separator bar to the menu
19944      * @return {Roo.menu.Item} The menu item that was added
19945      */
19946     addSeparator : function(){
19947         return this.addItem(new Roo.menu.Separator());
19948     },
19949
19950     /**
19951      * Adds an {@link Roo.Element} object to the menu
19952      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
19953      * @return {Roo.menu.Item} The menu item that was added
19954      */
19955     addElement : function(el){
19956         return this.addItem(new Roo.menu.BaseItem(el));
19957     },
19958
19959     /**
19960      * Adds an existing object based on {@link Roo.menu.Item} to the menu
19961      * @param {Roo.menu.Item} item The menu item to add
19962      * @return {Roo.menu.Item} The menu item that was added
19963      */
19964     addItem : function(item){
19965         this.items.add(item);
19966         if(this.ul){
19967             var li = document.createElement("li");
19968             li.className = "x-menu-list-item";
19969             this.ul.dom.appendChild(li);
19970             item.render(li, this);
19971             this.delayAutoWidth();
19972         }
19973         return item;
19974     },
19975
19976     /**
19977      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
19978      * @param {Object} config A MenuItem config object
19979      * @return {Roo.menu.Item} The menu item that was added
19980      */
19981     addMenuItem : function(config){
19982         if(!(config instanceof Roo.menu.Item)){
19983             if(typeof config.checked == "boolean"){ // must be check menu item config?
19984                 config = new Roo.menu.CheckItem(config);
19985             }else{
19986                 config = new Roo.menu.Item(config);
19987             }
19988         }
19989         return this.addItem(config);
19990     },
19991
19992     /**
19993      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
19994      * @param {String} text The text to display in the menu item
19995      * @return {Roo.menu.Item} The menu item that was added
19996      */
19997     addText : function(text){
19998         return this.addItem(new Roo.menu.TextItem({ text : text }));
19999     },
20000
20001     /**
20002      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
20003      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
20004      * @param {Roo.menu.Item} item The menu item to add
20005      * @return {Roo.menu.Item} The menu item that was added
20006      */
20007     insert : function(index, item){
20008         this.items.insert(index, item);
20009         if(this.ul){
20010             var li = document.createElement("li");
20011             li.className = "x-menu-list-item";
20012             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
20013             item.render(li, this);
20014             this.delayAutoWidth();
20015         }
20016         return item;
20017     },
20018
20019     /**
20020      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
20021      * @param {Roo.menu.Item} item The menu item to remove
20022      */
20023     remove : function(item){
20024         this.items.removeKey(item.id);
20025         item.destroy();
20026     },
20027
20028     /**
20029      * Removes and destroys all items in the menu
20030      */
20031     removeAll : function(){
20032         var f;
20033         while(f = this.items.first()){
20034             this.remove(f);
20035         }
20036     }
20037 });
20038
20039 // MenuNav is a private utility class used internally by the Menu
20040 Roo.menu.MenuNav = function(menu){
20041     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
20042     this.scope = this.menu = menu;
20043 };
20044
20045 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
20046     doRelay : function(e, h){
20047         var k = e.getKey();
20048         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
20049             this.menu.tryActivate(0, 1);
20050             return false;
20051         }
20052         return h.call(this.scope || this, e, this.menu);
20053     },
20054
20055     up : function(e, m){
20056         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
20057             m.tryActivate(m.items.length-1, -1);
20058         }
20059     },
20060
20061     down : function(e, m){
20062         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
20063             m.tryActivate(0, 1);
20064         }
20065     },
20066
20067     right : function(e, m){
20068         if(m.activeItem){
20069             m.activeItem.expandMenu(true);
20070         }
20071     },
20072
20073     left : function(e, m){
20074         m.hide();
20075         if(m.parentMenu && m.parentMenu.activeItem){
20076             m.parentMenu.activeItem.activate();
20077         }
20078     },
20079
20080     enter : function(e, m){
20081         if(m.activeItem){
20082             e.stopPropagation();
20083             m.activeItem.onClick(e);
20084             m.fireEvent("click", this, m.activeItem);
20085             return true;
20086         }
20087     }
20088 });/*
20089  * Based on:
20090  * Ext JS Library 1.1.1
20091  * Copyright(c) 2006-2007, Ext JS, LLC.
20092  *
20093  * Originally Released Under LGPL - original licence link has changed is not relivant.
20094  *
20095  * Fork - LGPL
20096  * <script type="text/javascript">
20097  */
20098  
20099 /**
20100  * @class Roo.menu.MenuMgr
20101  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
20102  * @singleton
20103  */
20104 Roo.menu.MenuMgr = function(){
20105    var menus, active, groups = {}, attached = false, lastShow = new Date();
20106
20107    // private - called when first menu is created
20108    function init(){
20109        menus = {};
20110        active = new Roo.util.MixedCollection();
20111        Roo.get(document).addKeyListener(27, function(){
20112            if(active.length > 0){
20113                hideAll();
20114            }
20115        });
20116    }
20117
20118    // private
20119    function hideAll(){
20120        if(active && active.length > 0){
20121            var c = active.clone();
20122            c.each(function(m){
20123                m.hide();
20124            });
20125        }
20126    }
20127
20128    // private
20129    function onHide(m){
20130        active.remove(m);
20131        if(active.length < 1){
20132            Roo.get(document).un("mousedown", onMouseDown);
20133            attached = false;
20134        }
20135    }
20136
20137    // private
20138    function onShow(m){
20139        var last = active.last();
20140        lastShow = new Date();
20141        active.add(m);
20142        if(!attached){
20143            Roo.get(document).on("mousedown", onMouseDown);
20144            attached = true;
20145        }
20146        if(m.parentMenu){
20147           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
20148           m.parentMenu.activeChild = m;
20149        }else if(last && last.isVisible()){
20150           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
20151        }
20152    }
20153
20154    // private
20155    function onBeforeHide(m){
20156        if(m.activeChild){
20157            m.activeChild.hide();
20158        }
20159        if(m.autoHideTimer){
20160            clearTimeout(m.autoHideTimer);
20161            delete m.autoHideTimer;
20162        }
20163    }
20164
20165    // private
20166    function onBeforeShow(m){
20167        var pm = m.parentMenu;
20168        if(!pm && !m.allowOtherMenus){
20169            hideAll();
20170        }else if(pm && pm.activeChild && active != m){
20171            pm.activeChild.hide();
20172        }
20173    }
20174
20175    // private
20176    function onMouseDown(e){
20177        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
20178            hideAll();
20179        }
20180    }
20181
20182    // private
20183    function onBeforeCheck(mi, state){
20184        if(state){
20185            var g = groups[mi.group];
20186            for(var i = 0, l = g.length; i < l; i++){
20187                if(g[i] != mi){
20188                    g[i].setChecked(false);
20189                }
20190            }
20191        }
20192    }
20193
20194    return {
20195
20196        /**
20197         * Hides all menus that are currently visible
20198         */
20199        hideAll : function(){
20200             hideAll();  
20201        },
20202
20203        // private
20204        register : function(menu){
20205            if(!menus){
20206                init();
20207            }
20208            menus[menu.id] = menu;
20209            menu.on("beforehide", onBeforeHide);
20210            menu.on("hide", onHide);
20211            menu.on("beforeshow", onBeforeShow);
20212            menu.on("show", onShow);
20213            var g = menu.group;
20214            if(g && menu.events["checkchange"]){
20215                if(!groups[g]){
20216                    groups[g] = [];
20217                }
20218                groups[g].push(menu);
20219                menu.on("checkchange", onCheck);
20220            }
20221        },
20222
20223         /**
20224          * Returns a {@link Roo.menu.Menu} object
20225          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
20226          * be used to generate and return a new Menu instance.
20227          */
20228        get : function(menu){
20229            if(typeof menu == "string"){ // menu id
20230                return menus[menu];
20231            }else if(menu.events){  // menu instance
20232                return menu;
20233            }else if(typeof menu.length == 'number'){ // array of menu items?
20234                return new Roo.menu.Menu({items:menu});
20235            }else{ // otherwise, must be a config
20236                return new Roo.menu.Menu(menu);
20237            }
20238        },
20239
20240        // private
20241        unregister : function(menu){
20242            delete menus[menu.id];
20243            menu.un("beforehide", onBeforeHide);
20244            menu.un("hide", onHide);
20245            menu.un("beforeshow", onBeforeShow);
20246            menu.un("show", onShow);
20247            var g = menu.group;
20248            if(g && menu.events["checkchange"]){
20249                groups[g].remove(menu);
20250                menu.un("checkchange", onCheck);
20251            }
20252        },
20253
20254        // private
20255        registerCheckable : function(menuItem){
20256            var g = menuItem.group;
20257            if(g){
20258                if(!groups[g]){
20259                    groups[g] = [];
20260                }
20261                groups[g].push(menuItem);
20262                menuItem.on("beforecheckchange", onBeforeCheck);
20263            }
20264        },
20265
20266        // private
20267        unregisterCheckable : function(menuItem){
20268            var g = menuItem.group;
20269            if(g){
20270                groups[g].remove(menuItem);
20271                menuItem.un("beforecheckchange", onBeforeCheck);
20272            }
20273        }
20274    };
20275 }();/*
20276  * Based on:
20277  * Ext JS Library 1.1.1
20278  * Copyright(c) 2006-2007, Ext JS, LLC.
20279  *
20280  * Originally Released Under LGPL - original licence link has changed is not relivant.
20281  *
20282  * Fork - LGPL
20283  * <script type="text/javascript">
20284  */
20285  
20286
20287 /**
20288  * @class Roo.menu.BaseItem
20289  * @extends Roo.Component
20290  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
20291  * management and base configuration options shared by all menu components.
20292  * @constructor
20293  * Creates a new BaseItem
20294  * @param {Object} config Configuration options
20295  */
20296 Roo.menu.BaseItem = function(config){
20297     Roo.menu.BaseItem.superclass.constructor.call(this, config);
20298
20299     this.addEvents({
20300         /**
20301          * @event click
20302          * Fires when this item is clicked
20303          * @param {Roo.menu.BaseItem} this
20304          * @param {Roo.EventObject} e
20305          */
20306         click: true,
20307         /**
20308          * @event activate
20309          * Fires when this item is activated
20310          * @param {Roo.menu.BaseItem} this
20311          */
20312         activate : true,
20313         /**
20314          * @event deactivate
20315          * Fires when this item is deactivated
20316          * @param {Roo.menu.BaseItem} this
20317          */
20318         deactivate : true
20319     });
20320
20321     if(this.handler){
20322         this.on("click", this.handler, this.scope, true);
20323     }
20324 };
20325
20326 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
20327     /**
20328      * @cfg {Function} handler
20329      * A function that will handle the click event of this menu item (defaults to undefined)
20330      */
20331     /**
20332      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
20333      */
20334     canActivate : false,
20335     /**
20336      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
20337      */
20338     activeClass : "x-menu-item-active",
20339     /**
20340      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
20341      */
20342     hideOnClick : true,
20343     /**
20344      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
20345      */
20346     hideDelay : 100,
20347
20348     // private
20349     ctype: "Roo.menu.BaseItem",
20350
20351     // private
20352     actionMode : "container",
20353
20354     // private
20355     render : function(container, parentMenu){
20356         this.parentMenu = parentMenu;
20357         Roo.menu.BaseItem.superclass.render.call(this, container);
20358         this.container.menuItemId = this.id;
20359     },
20360
20361     // private
20362     onRender : function(container, position){
20363         this.el = Roo.get(this.el);
20364         container.dom.appendChild(this.el.dom);
20365     },
20366
20367     // private
20368     onClick : function(e){
20369         if(!this.disabled && this.fireEvent("click", this, e) !== false
20370                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
20371             this.handleClick(e);
20372         }else{
20373             e.stopEvent();
20374         }
20375     },
20376
20377     // private
20378     activate : function(){
20379         if(this.disabled){
20380             return false;
20381         }
20382         var li = this.container;
20383         li.addClass(this.activeClass);
20384         this.region = li.getRegion().adjust(2, 2, -2, -2);
20385         this.fireEvent("activate", this);
20386         return true;
20387     },
20388
20389     // private
20390     deactivate : function(){
20391         this.container.removeClass(this.activeClass);
20392         this.fireEvent("deactivate", this);
20393     },
20394
20395     // private
20396     shouldDeactivate : function(e){
20397         return !this.region || !this.region.contains(e.getPoint());
20398     },
20399
20400     // private
20401     handleClick : function(e){
20402         if(this.hideOnClick){
20403             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
20404         }
20405     },
20406
20407     // private
20408     expandMenu : function(autoActivate){
20409         // do nothing
20410     },
20411
20412     // private
20413     hideMenu : function(){
20414         // do nothing
20415     }
20416 });/*
20417  * Based on:
20418  * Ext JS Library 1.1.1
20419  * Copyright(c) 2006-2007, Ext JS, LLC.
20420  *
20421  * Originally Released Under LGPL - original licence link has changed is not relivant.
20422  *
20423  * Fork - LGPL
20424  * <script type="text/javascript">
20425  */
20426  
20427 /**
20428  * @class Roo.menu.Adapter
20429  * @extends Roo.menu.BaseItem
20430  * 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.
20431  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
20432  * @constructor
20433  * Creates a new Adapter
20434  * @param {Object} config Configuration options
20435  */
20436 Roo.menu.Adapter = function(component, config){
20437     Roo.menu.Adapter.superclass.constructor.call(this, config);
20438     this.component = component;
20439 };
20440 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
20441     // private
20442     canActivate : true,
20443
20444     // private
20445     onRender : function(container, position){
20446         this.component.render(container);
20447         this.el = this.component.getEl();
20448     },
20449
20450     // private
20451     activate : function(){
20452         if(this.disabled){
20453             return false;
20454         }
20455         this.component.focus();
20456         this.fireEvent("activate", this);
20457         return true;
20458     },
20459
20460     // private
20461     deactivate : function(){
20462         this.fireEvent("deactivate", this);
20463     },
20464
20465     // private
20466     disable : function(){
20467         this.component.disable();
20468         Roo.menu.Adapter.superclass.disable.call(this);
20469     },
20470
20471     // private
20472     enable : function(){
20473         this.component.enable();
20474         Roo.menu.Adapter.superclass.enable.call(this);
20475     }
20476 });/*
20477  * Based on:
20478  * Ext JS Library 1.1.1
20479  * Copyright(c) 2006-2007, Ext JS, LLC.
20480  *
20481  * Originally Released Under LGPL - original licence link has changed is not relivant.
20482  *
20483  * Fork - LGPL
20484  * <script type="text/javascript">
20485  */
20486
20487 /**
20488  * @class Roo.menu.TextItem
20489  * @extends Roo.menu.BaseItem
20490  * Adds a static text string to a menu, usually used as either a heading or group separator.
20491  * Note: old style constructor with text is still supported.
20492  * 
20493  * @constructor
20494  * Creates a new TextItem
20495  * @param {Object} cfg Configuration
20496  */
20497 Roo.menu.TextItem = function(cfg){
20498     if (typeof(cfg) == 'string') {
20499         this.text = cfg;
20500     } else {
20501         Roo.apply(this,cfg);
20502     }
20503     
20504     Roo.menu.TextItem.superclass.constructor.call(this);
20505 };
20506
20507 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20508     /**
20509      * @cfg {Boolean} text Text to show on item.
20510      */
20511     text : '',
20512     
20513     /**
20514      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20515      */
20516     hideOnClick : false,
20517     /**
20518      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20519      */
20520     itemCls : "x-menu-text",
20521
20522     // private
20523     onRender : function(){
20524         var s = document.createElement("span");
20525         s.className = this.itemCls;
20526         s.innerHTML = this.text;
20527         this.el = s;
20528         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20529     }
20530 });/*
20531  * Based on:
20532  * Ext JS Library 1.1.1
20533  * Copyright(c) 2006-2007, Ext JS, LLC.
20534  *
20535  * Originally Released Under LGPL - original licence link has changed is not relivant.
20536  *
20537  * Fork - LGPL
20538  * <script type="text/javascript">
20539  */
20540
20541 /**
20542  * @class Roo.menu.Separator
20543  * @extends Roo.menu.BaseItem
20544  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20545  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20546  * @constructor
20547  * @param {Object} config Configuration options
20548  */
20549 Roo.menu.Separator = function(config){
20550     Roo.menu.Separator.superclass.constructor.call(this, config);
20551 };
20552
20553 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20554     /**
20555      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20556      */
20557     itemCls : "x-menu-sep",
20558     /**
20559      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20560      */
20561     hideOnClick : false,
20562
20563     // private
20564     onRender : function(li){
20565         var s = document.createElement("span");
20566         s.className = this.itemCls;
20567         s.innerHTML = "&#160;";
20568         this.el = s;
20569         li.addClass("x-menu-sep-li");
20570         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20571     }
20572 });/*
20573  * Based on:
20574  * Ext JS Library 1.1.1
20575  * Copyright(c) 2006-2007, Ext JS, LLC.
20576  *
20577  * Originally Released Under LGPL - original licence link has changed is not relivant.
20578  *
20579  * Fork - LGPL
20580  * <script type="text/javascript">
20581  */
20582 /**
20583  * @class Roo.menu.Item
20584  * @extends Roo.menu.BaseItem
20585  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20586  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20587  * activation and click handling.
20588  * @constructor
20589  * Creates a new Item
20590  * @param {Object} config Configuration options
20591  */
20592 Roo.menu.Item = function(config){
20593     Roo.menu.Item.superclass.constructor.call(this, config);
20594     if(this.menu){
20595         this.menu = Roo.menu.MenuMgr.get(this.menu);
20596     }
20597 };
20598 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20599     
20600     /**
20601      * @cfg {String} text
20602      * The text to show on the menu item.
20603      */
20604     text: '',
20605      /**
20606      * @cfg {String} HTML to render in menu
20607      * The text to show on the menu item (HTML version).
20608      */
20609     html: '',
20610     /**
20611      * @cfg {String} icon
20612      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20613      */
20614     icon: undefined,
20615     /**
20616      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20617      */
20618     itemCls : "x-menu-item",
20619     /**
20620      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20621      */
20622     canActivate : true,
20623     /**
20624      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20625      */
20626     showDelay: 200,
20627     // doc'd in BaseItem
20628     hideDelay: 200,
20629
20630     // private
20631     ctype: "Roo.menu.Item",
20632     
20633     // private
20634     onRender : function(container, position){
20635         var el = document.createElement("a");
20636         el.hideFocus = true;
20637         el.unselectable = "on";
20638         el.href = this.href || "#";
20639         if(this.hrefTarget){
20640             el.target = this.hrefTarget;
20641         }
20642         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
20643         
20644         var html = this.html.length ? this.html  : String.format('{0}',this.text);
20645         
20646         el.innerHTML = String.format(
20647                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
20648                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
20649         this.el = el;
20650         Roo.menu.Item.superclass.onRender.call(this, container, position);
20651     },
20652
20653     /**
20654      * Sets the text to display in this menu item
20655      * @param {String} text The text to display
20656      * @param {Boolean} isHTML true to indicate text is pure html.
20657      */
20658     setText : function(text, isHTML){
20659         if (isHTML) {
20660             this.html = text;
20661         } else {
20662             this.text = text;
20663             this.html = '';
20664         }
20665         if(this.rendered){
20666             var html = this.html.length ? this.html  : String.format('{0}',this.text);
20667      
20668             this.el.update(String.format(
20669                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
20670                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20671             this.parentMenu.autoWidth();
20672         }
20673     },
20674
20675     // private
20676     handleClick : function(e){
20677         if(!this.href){ // if no link defined, stop the event automatically
20678             e.stopEvent();
20679         }
20680         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20681     },
20682
20683     // private
20684     activate : function(autoExpand){
20685         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20686             this.focus();
20687             if(autoExpand){
20688                 this.expandMenu();
20689             }
20690         }
20691         return true;
20692     },
20693
20694     // private
20695     shouldDeactivate : function(e){
20696         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20697             if(this.menu && this.menu.isVisible()){
20698                 return !this.menu.getEl().getRegion().contains(e.getPoint());
20699             }
20700             return true;
20701         }
20702         return false;
20703     },
20704
20705     // private
20706     deactivate : function(){
20707         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20708         this.hideMenu();
20709     },
20710
20711     // private
20712     expandMenu : function(autoActivate){
20713         if(!this.disabled && this.menu){
20714             clearTimeout(this.hideTimer);
20715             delete this.hideTimer;
20716             if(!this.menu.isVisible() && !this.showTimer){
20717                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20718             }else if (this.menu.isVisible() && autoActivate){
20719                 this.menu.tryActivate(0, 1);
20720             }
20721         }
20722     },
20723
20724     // private
20725     deferExpand : function(autoActivate){
20726         delete this.showTimer;
20727         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20728         if(autoActivate){
20729             this.menu.tryActivate(0, 1);
20730         }
20731     },
20732
20733     // private
20734     hideMenu : function(){
20735         clearTimeout(this.showTimer);
20736         delete this.showTimer;
20737         if(!this.hideTimer && this.menu && this.menu.isVisible()){
20738             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20739         }
20740     },
20741
20742     // private
20743     deferHide : function(){
20744         delete this.hideTimer;
20745         this.menu.hide();
20746     }
20747 });/*
20748  * Based on:
20749  * Ext JS Library 1.1.1
20750  * Copyright(c) 2006-2007, Ext JS, LLC.
20751  *
20752  * Originally Released Under LGPL - original licence link has changed is not relivant.
20753  *
20754  * Fork - LGPL
20755  * <script type="text/javascript">
20756  */
20757  
20758 /**
20759  * @class Roo.menu.CheckItem
20760  * @extends Roo.menu.Item
20761  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20762  * @constructor
20763  * Creates a new CheckItem
20764  * @param {Object} config Configuration options
20765  */
20766 Roo.menu.CheckItem = function(config){
20767     Roo.menu.CheckItem.superclass.constructor.call(this, config);
20768     this.addEvents({
20769         /**
20770          * @event beforecheckchange
20771          * Fires before the checked value is set, providing an opportunity to cancel if needed
20772          * @param {Roo.menu.CheckItem} this
20773          * @param {Boolean} checked The new checked value that will be set
20774          */
20775         "beforecheckchange" : true,
20776         /**
20777          * @event checkchange
20778          * Fires after the checked value has been set
20779          * @param {Roo.menu.CheckItem} this
20780          * @param {Boolean} checked The checked value that was set
20781          */
20782         "checkchange" : true
20783     });
20784     if(this.checkHandler){
20785         this.on('checkchange', this.checkHandler, this.scope);
20786     }
20787 };
20788 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
20789     /**
20790      * @cfg {String} group
20791      * All check items with the same group name will automatically be grouped into a single-select
20792      * radio button group (defaults to '')
20793      */
20794     /**
20795      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
20796      */
20797     itemCls : "x-menu-item x-menu-check-item",
20798     /**
20799      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
20800      */
20801     groupClass : "x-menu-group-item",
20802
20803     /**
20804      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
20805      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
20806      * initialized with checked = true will be rendered as checked.
20807      */
20808     checked: false,
20809
20810     // private
20811     ctype: "Roo.menu.CheckItem",
20812
20813     // private
20814     onRender : function(c){
20815         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
20816         if(this.group){
20817             this.el.addClass(this.groupClass);
20818         }
20819         Roo.menu.MenuMgr.registerCheckable(this);
20820         if(this.checked){
20821             this.checked = false;
20822             this.setChecked(true, true);
20823         }
20824     },
20825
20826     // private
20827     destroy : function(){
20828         if(this.rendered){
20829             Roo.menu.MenuMgr.unregisterCheckable(this);
20830         }
20831         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
20832     },
20833
20834     /**
20835      * Set the checked state of this item
20836      * @param {Boolean} checked The new checked value
20837      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
20838      */
20839     setChecked : function(state, suppressEvent){
20840         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
20841             if(this.container){
20842                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
20843             }
20844             this.checked = state;
20845             if(suppressEvent !== true){
20846                 this.fireEvent("checkchange", this, state);
20847             }
20848         }
20849     },
20850
20851     // private
20852     handleClick : function(e){
20853        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
20854            this.setChecked(!this.checked);
20855        }
20856        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
20857     }
20858 });/*
20859  * Based on:
20860  * Ext JS Library 1.1.1
20861  * Copyright(c) 2006-2007, Ext JS, LLC.
20862  *
20863  * Originally Released Under LGPL - original licence link has changed is not relivant.
20864  *
20865  * Fork - LGPL
20866  * <script type="text/javascript">
20867  */
20868  
20869 /**
20870  * @class Roo.menu.DateItem
20871  * @extends Roo.menu.Adapter
20872  * A menu item that wraps the {@link Roo.DatPicker} component.
20873  * @constructor
20874  * Creates a new DateItem
20875  * @param {Object} config Configuration options
20876  */
20877 Roo.menu.DateItem = function(config){
20878     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
20879     /** The Roo.DatePicker object @type Roo.DatePicker */
20880     this.picker = this.component;
20881     this.addEvents({select: true});
20882     
20883     this.picker.on("render", function(picker){
20884         picker.getEl().swallowEvent("click");
20885         picker.container.addClass("x-menu-date-item");
20886     });
20887
20888     this.picker.on("select", this.onSelect, this);
20889 };
20890
20891 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
20892     // private
20893     onSelect : function(picker, date){
20894         this.fireEvent("select", this, date, picker);
20895         Roo.menu.DateItem.superclass.handleClick.call(this);
20896     }
20897 });/*
20898  * Based on:
20899  * Ext JS Library 1.1.1
20900  * Copyright(c) 2006-2007, Ext JS, LLC.
20901  *
20902  * Originally Released Under LGPL - original licence link has changed is not relivant.
20903  *
20904  * Fork - LGPL
20905  * <script type="text/javascript">
20906  */
20907  
20908 /**
20909  * @class Roo.menu.ColorItem
20910  * @extends Roo.menu.Adapter
20911  * A menu item that wraps the {@link Roo.ColorPalette} component.
20912  * @constructor
20913  * Creates a new ColorItem
20914  * @param {Object} config Configuration options
20915  */
20916 Roo.menu.ColorItem = function(config){
20917     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
20918     /** The Roo.ColorPalette object @type Roo.ColorPalette */
20919     this.palette = this.component;
20920     this.relayEvents(this.palette, ["select"]);
20921     if(this.selectHandler){
20922         this.on('select', this.selectHandler, this.scope);
20923     }
20924 };
20925 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
20926  * Based on:
20927  * Ext JS Library 1.1.1
20928  * Copyright(c) 2006-2007, Ext JS, LLC.
20929  *
20930  * Originally Released Under LGPL - original licence link has changed is not relivant.
20931  *
20932  * Fork - LGPL
20933  * <script type="text/javascript">
20934  */
20935  
20936
20937 /**
20938  * @class Roo.menu.DateMenu
20939  * @extends Roo.menu.Menu
20940  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
20941  * @constructor
20942  * Creates a new DateMenu
20943  * @param {Object} config Configuration options
20944  */
20945 Roo.menu.DateMenu = function(config){
20946     Roo.menu.DateMenu.superclass.constructor.call(this, config);
20947     this.plain = true;
20948     var di = new Roo.menu.DateItem(config);
20949     this.add(di);
20950     /**
20951      * The {@link Roo.DatePicker} instance for this DateMenu
20952      * @type DatePicker
20953      */
20954     this.picker = di.picker;
20955     /**
20956      * @event select
20957      * @param {DatePicker} picker
20958      * @param {Date} date
20959      */
20960     this.relayEvents(di, ["select"]);
20961
20962     this.on('beforeshow', function(){
20963         if(this.picker){
20964             this.picker.hideMonthPicker(true);
20965         }
20966     }, this);
20967 };
20968 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
20969     cls:'x-date-menu'
20970 });/*
20971  * Based on:
20972  * Ext JS Library 1.1.1
20973  * Copyright(c) 2006-2007, Ext JS, LLC.
20974  *
20975  * Originally Released Under LGPL - original licence link has changed is not relivant.
20976  *
20977  * Fork - LGPL
20978  * <script type="text/javascript">
20979  */
20980  
20981
20982 /**
20983  * @class Roo.menu.ColorMenu
20984  * @extends Roo.menu.Menu
20985  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
20986  * @constructor
20987  * Creates a new ColorMenu
20988  * @param {Object} config Configuration options
20989  */
20990 Roo.menu.ColorMenu = function(config){
20991     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
20992     this.plain = true;
20993     var ci = new Roo.menu.ColorItem(config);
20994     this.add(ci);
20995     /**
20996      * The {@link Roo.ColorPalette} instance for this ColorMenu
20997      * @type ColorPalette
20998      */
20999     this.palette = ci.palette;
21000     /**
21001      * @event select
21002      * @param {ColorPalette} palette
21003      * @param {String} color
21004      */
21005     this.relayEvents(ci, ["select"]);
21006 };
21007 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
21008  * Based on:
21009  * Ext JS Library 1.1.1
21010  * Copyright(c) 2006-2007, Ext JS, LLC.
21011  *
21012  * Originally Released Under LGPL - original licence link has changed is not relivant.
21013  *
21014  * Fork - LGPL
21015  * <script type="text/javascript">
21016  */
21017  
21018 /**
21019  * @class Roo.form.Field
21020  * @extends Roo.BoxComponent
21021  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
21022  * @constructor
21023  * Creates a new Field
21024  * @param {Object} config Configuration options
21025  */
21026 Roo.form.Field = function(config){
21027     Roo.form.Field.superclass.constructor.call(this, config);
21028 };
21029
21030 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
21031     /**
21032      * @cfg {String} fieldLabel Label to use when rendering a form.
21033      */
21034        /**
21035      * @cfg {String} qtip Mouse over tip
21036      */
21037      
21038     /**
21039      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
21040      */
21041     invalidClass : "x-form-invalid",
21042     /**
21043      * @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")
21044      */
21045     invalidText : "The value in this field is invalid",
21046     /**
21047      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
21048      */
21049     focusClass : "x-form-focus",
21050     /**
21051      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
21052       automatic validation (defaults to "keyup").
21053      */
21054     validationEvent : "keyup",
21055     /**
21056      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
21057      */
21058     validateOnBlur : true,
21059     /**
21060      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
21061      */
21062     validationDelay : 250,
21063     /**
21064      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21065      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
21066      */
21067     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
21068     /**
21069      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
21070      */
21071     fieldClass : "x-form-field",
21072     /**
21073      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
21074      *<pre>
21075 Value         Description
21076 -----------   ----------------------------------------------------------------------
21077 qtip          Display a quick tip when the user hovers over the field
21078 title         Display a default browser title attribute popup
21079 under         Add a block div beneath the field containing the error text
21080 side          Add an error icon to the right of the field with a popup on hover
21081 [element id]  Add the error text directly to the innerHTML of the specified element
21082 </pre>
21083      */
21084     msgTarget : 'qtip',
21085     /**
21086      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
21087      */
21088     msgFx : 'normal',
21089
21090     /**
21091      * @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.
21092      */
21093     readOnly : false,
21094
21095     /**
21096      * @cfg {Boolean} disabled True to disable the field (defaults to false).
21097      */
21098     disabled : false,
21099
21100     /**
21101      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
21102      */
21103     inputType : undefined,
21104     
21105     /**
21106      * @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).
21107          */
21108         tabIndex : undefined,
21109         
21110     // private
21111     isFormField : true,
21112
21113     // private
21114     hasFocus : false,
21115     /**
21116      * @property {Roo.Element} fieldEl
21117      * Element Containing the rendered Field (with label etc.)
21118      */
21119     /**
21120      * @cfg {Mixed} value A value to initialize this field with.
21121      */
21122     value : undefined,
21123
21124     /**
21125      * @cfg {String} name The field's HTML name attribute.
21126      */
21127     /**
21128      * @cfg {String} cls A CSS class to apply to the field's underlying element.
21129      */
21130
21131         // private ??
21132         initComponent : function(){
21133         Roo.form.Field.superclass.initComponent.call(this);
21134         this.addEvents({
21135             /**
21136              * @event focus
21137              * Fires when this field receives input focus.
21138              * @param {Roo.form.Field} this
21139              */
21140             focus : true,
21141             /**
21142              * @event blur
21143              * Fires when this field loses input focus.
21144              * @param {Roo.form.Field} this
21145              */
21146             blur : true,
21147             /**
21148              * @event specialkey
21149              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
21150              * {@link Roo.EventObject#getKey} to determine which key was pressed.
21151              * @param {Roo.form.Field} this
21152              * @param {Roo.EventObject} e The event object
21153              */
21154             specialkey : true,
21155             /**
21156              * @event change
21157              * Fires just before the field blurs if the field value has changed.
21158              * @param {Roo.form.Field} this
21159              * @param {Mixed} newValue The new value
21160              * @param {Mixed} oldValue The original value
21161              */
21162             change : true,
21163             /**
21164              * @event invalid
21165              * Fires after the field has been marked as invalid.
21166              * @param {Roo.form.Field} this
21167              * @param {String} msg The validation message
21168              */
21169             invalid : true,
21170             /**
21171              * @event valid
21172              * Fires after the field has been validated with no errors.
21173              * @param {Roo.form.Field} this
21174              */
21175             valid : true,
21176              /**
21177              * @event keyup
21178              * Fires after the key up
21179              * @param {Roo.form.Field} this
21180              * @param {Roo.EventObject}  e The event Object
21181              */
21182             keyup : true
21183         });
21184     },
21185
21186     /**
21187      * Returns the name attribute of the field if available
21188      * @return {String} name The field name
21189      */
21190     getName: function(){
21191          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
21192     },
21193
21194     // private
21195     onRender : function(ct, position){
21196         Roo.form.Field.superclass.onRender.call(this, ct, position);
21197         if(!this.el){
21198             var cfg = this.getAutoCreate();
21199             if(!cfg.name){
21200                 cfg.name = this.name || this.id;
21201             }
21202             if(this.inputType){
21203                 cfg.type = this.inputType;
21204             }
21205             this.el = ct.createChild(cfg, position);
21206         }
21207         var type = this.el.dom.type;
21208         if(type){
21209             if(type == 'password'){
21210                 type = 'text';
21211             }
21212             this.el.addClass('x-form-'+type);
21213         }
21214         if(this.readOnly){
21215             this.el.dom.readOnly = true;
21216         }
21217         if(this.tabIndex !== undefined){
21218             this.el.dom.setAttribute('tabIndex', this.tabIndex);
21219         }
21220
21221         this.el.addClass([this.fieldClass, this.cls]);
21222         this.initValue();
21223     },
21224
21225     /**
21226      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
21227      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
21228      * @return {Roo.form.Field} this
21229      */
21230     applyTo : function(target){
21231         this.allowDomMove = false;
21232         this.el = Roo.get(target);
21233         this.render(this.el.dom.parentNode);
21234         return this;
21235     },
21236
21237     // private
21238     initValue : function(){
21239         if(this.value !== undefined){
21240             this.setValue(this.value);
21241         }else if(this.el.dom.value.length > 0){
21242             this.setValue(this.el.dom.value);
21243         }
21244     },
21245
21246     /**
21247      * Returns true if this field has been changed since it was originally loaded and is not disabled.
21248      */
21249     isDirty : function() {
21250         if(this.disabled) {
21251             return false;
21252         }
21253         return String(this.getValue()) !== String(this.originalValue);
21254     },
21255
21256     // private
21257     afterRender : function(){
21258         Roo.form.Field.superclass.afterRender.call(this);
21259         this.initEvents();
21260     },
21261
21262     // private
21263     fireKey : function(e){
21264         //Roo.log('field ' + e.getKey());
21265         if(e.isNavKeyPress()){
21266             this.fireEvent("specialkey", this, e);
21267         }
21268     },
21269
21270     /**
21271      * Resets the current field value to the originally loaded value and clears any validation messages
21272      */
21273     reset : function(){
21274         this.setValue(this.originalValue);
21275         this.clearInvalid();
21276     },
21277
21278     // private
21279     initEvents : function(){
21280         // safari killled keypress - so keydown is now used..
21281         this.el.on("keydown" , this.fireKey,  this);
21282         this.el.on("focus", this.onFocus,  this);
21283         this.el.on("blur", this.onBlur,  this);
21284         this.el.relayEvent('keyup', this);
21285
21286         // reference to original value for reset
21287         this.originalValue = this.getValue();
21288     },
21289
21290     // private
21291     onFocus : function(){
21292         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21293             this.el.addClass(this.focusClass);
21294         }
21295         if(!this.hasFocus){
21296             this.hasFocus = true;
21297             this.startValue = this.getValue();
21298             this.fireEvent("focus", this);
21299         }
21300     },
21301
21302     beforeBlur : Roo.emptyFn,
21303
21304     // private
21305     onBlur : function(){
21306         this.beforeBlur();
21307         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21308             this.el.removeClass(this.focusClass);
21309         }
21310         this.hasFocus = false;
21311         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
21312             this.validate();
21313         }
21314         var v = this.getValue();
21315         if(String(v) !== String(this.startValue)){
21316             this.fireEvent('change', this, v, this.startValue);
21317         }
21318         this.fireEvent("blur", this);
21319     },
21320
21321     /**
21322      * Returns whether or not the field value is currently valid
21323      * @param {Boolean} preventMark True to disable marking the field invalid
21324      * @return {Boolean} True if the value is valid, else false
21325      */
21326     isValid : function(preventMark){
21327         if(this.disabled){
21328             return true;
21329         }
21330         var restore = this.preventMark;
21331         this.preventMark = preventMark === true;
21332         var v = this.validateValue(this.processValue(this.getRawValue()));
21333         this.preventMark = restore;
21334         return v;
21335     },
21336
21337     /**
21338      * Validates the field value
21339      * @return {Boolean} True if the value is valid, else false
21340      */
21341     validate : function(){
21342         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
21343             this.clearInvalid();
21344             return true;
21345         }
21346         return false;
21347     },
21348
21349     processValue : function(value){
21350         return value;
21351     },
21352
21353     // private
21354     // Subclasses should provide the validation implementation by overriding this
21355     validateValue : function(value){
21356         return true;
21357     },
21358
21359     /**
21360      * Mark this field as invalid
21361      * @param {String} msg The validation message
21362      */
21363     markInvalid : function(msg){
21364         if(!this.rendered || this.preventMark){ // not rendered
21365             return;
21366         }
21367         this.el.addClass(this.invalidClass);
21368         msg = msg || this.invalidText;
21369         switch(this.msgTarget){
21370             case 'qtip':
21371                 this.el.dom.qtip = msg;
21372                 this.el.dom.qclass = 'x-form-invalid-tip';
21373                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
21374                     Roo.QuickTips.enable();
21375                 }
21376                 break;
21377             case 'title':
21378                 this.el.dom.title = msg;
21379                 break;
21380             case 'under':
21381                 if(!this.errorEl){
21382                     var elp = this.el.findParent('.x-form-element', 5, true);
21383                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
21384                     this.errorEl.setWidth(elp.getWidth(true)-20);
21385                 }
21386                 this.errorEl.update(msg);
21387                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
21388                 break;
21389             case 'side':
21390                 if(!this.errorIcon){
21391                     var elp = this.el.findParent('.x-form-element', 5, true);
21392                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
21393                 }
21394                 this.alignErrorIcon();
21395                 this.errorIcon.dom.qtip = msg;
21396                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
21397                 this.errorIcon.show();
21398                 this.on('resize', this.alignErrorIcon, this);
21399                 break;
21400             default:
21401                 var t = Roo.getDom(this.msgTarget);
21402                 t.innerHTML = msg;
21403                 t.style.display = this.msgDisplay;
21404                 break;
21405         }
21406         this.fireEvent('invalid', this, msg);
21407     },
21408
21409     // private
21410     alignErrorIcon : function(){
21411         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
21412     },
21413
21414     /**
21415      * Clear any invalid styles/messages for this field
21416      */
21417     clearInvalid : function(){
21418         if(!this.rendered || this.preventMark){ // not rendered
21419             return;
21420         }
21421         this.el.removeClass(this.invalidClass);
21422         switch(this.msgTarget){
21423             case 'qtip':
21424                 this.el.dom.qtip = '';
21425                 break;
21426             case 'title':
21427                 this.el.dom.title = '';
21428                 break;
21429             case 'under':
21430                 if(this.errorEl){
21431                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
21432                 }
21433                 break;
21434             case 'side':
21435                 if(this.errorIcon){
21436                     this.errorIcon.dom.qtip = '';
21437                     this.errorIcon.hide();
21438                     this.un('resize', this.alignErrorIcon, this);
21439                 }
21440                 break;
21441             default:
21442                 var t = Roo.getDom(this.msgTarget);
21443                 t.innerHTML = '';
21444                 t.style.display = 'none';
21445                 break;
21446         }
21447         this.fireEvent('valid', this);
21448     },
21449
21450     /**
21451      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
21452      * @return {Mixed} value The field value
21453      */
21454     getRawValue : function(){
21455         var v = this.el.getValue();
21456         if(v === this.emptyText){
21457             v = '';
21458         }
21459         return v;
21460     },
21461
21462     /**
21463      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
21464      * @return {Mixed} value The field value
21465      */
21466     getValue : function(){
21467         var v = this.el.getValue();
21468         if(v === this.emptyText || v === undefined){
21469             v = '';
21470         }
21471         return v;
21472     },
21473
21474     /**
21475      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
21476      * @param {Mixed} value The value to set
21477      */
21478     setRawValue : function(v){
21479         return this.el.dom.value = (v === null || v === undefined ? '' : v);
21480     },
21481
21482     /**
21483      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
21484      * @param {Mixed} value The value to set
21485      */
21486     setValue : function(v){
21487         this.value = v;
21488         if(this.rendered){
21489             this.el.dom.value = (v === null || v === undefined ? '' : v);
21490              this.validate();
21491         }
21492     },
21493
21494     adjustSize : function(w, h){
21495         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21496         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21497         return s;
21498     },
21499
21500     adjustWidth : function(tag, w){
21501         tag = tag.toLowerCase();
21502         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21503             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21504                 if(tag == 'input'){
21505                     return w + 2;
21506                 }
21507                 if(tag = 'textarea'){
21508                     return w-2;
21509                 }
21510             }else if(Roo.isOpera){
21511                 if(tag == 'input'){
21512                     return w + 2;
21513                 }
21514                 if(tag = 'textarea'){
21515                     return w-2;
21516                 }
21517             }
21518         }
21519         return w;
21520     }
21521 });
21522
21523
21524 // anything other than normal should be considered experimental
21525 Roo.form.Field.msgFx = {
21526     normal : {
21527         show: function(msgEl, f){
21528             msgEl.setDisplayed('block');
21529         },
21530
21531         hide : function(msgEl, f){
21532             msgEl.setDisplayed(false).update('');
21533         }
21534     },
21535
21536     slide : {
21537         show: function(msgEl, f){
21538             msgEl.slideIn('t', {stopFx:true});
21539         },
21540
21541         hide : function(msgEl, f){
21542             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21543         }
21544     },
21545
21546     slideRight : {
21547         show: function(msgEl, f){
21548             msgEl.fixDisplay();
21549             msgEl.alignTo(f.el, 'tl-tr');
21550             msgEl.slideIn('l', {stopFx:true});
21551         },
21552
21553         hide : function(msgEl, f){
21554             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21555         }
21556     }
21557 };/*
21558  * Based on:
21559  * Ext JS Library 1.1.1
21560  * Copyright(c) 2006-2007, Ext JS, LLC.
21561  *
21562  * Originally Released Under LGPL - original licence link has changed is not relivant.
21563  *
21564  * Fork - LGPL
21565  * <script type="text/javascript">
21566  */
21567  
21568
21569 /**
21570  * @class Roo.form.TextField
21571  * @extends Roo.form.Field
21572  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
21573  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21574  * @constructor
21575  * Creates a new TextField
21576  * @param {Object} config Configuration options
21577  */
21578 Roo.form.TextField = function(config){
21579     Roo.form.TextField.superclass.constructor.call(this, config);
21580     this.addEvents({
21581         /**
21582          * @event autosize
21583          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
21584          * according to the default logic, but this event provides a hook for the developer to apply additional
21585          * logic at runtime to resize the field if needed.
21586              * @param {Roo.form.Field} this This text field
21587              * @param {Number} width The new field width
21588              */
21589         autosize : true
21590     });
21591 };
21592
21593 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
21594     /**
21595      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21596      */
21597     grow : false,
21598     /**
21599      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21600      */
21601     growMin : 30,
21602     /**
21603      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21604      */
21605     growMax : 800,
21606     /**
21607      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21608      */
21609     vtype : null,
21610     /**
21611      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21612      */
21613     maskRe : null,
21614     /**
21615      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21616      */
21617     disableKeyFilter : false,
21618     /**
21619      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21620      */
21621     allowBlank : true,
21622     /**
21623      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21624      */
21625     minLength : 0,
21626     /**
21627      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21628      */
21629     maxLength : Number.MAX_VALUE,
21630     /**
21631      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21632      */
21633     minLengthText : "The minimum length for this field is {0}",
21634     /**
21635      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21636      */
21637     maxLengthText : "The maximum length for this field is {0}",
21638     /**
21639      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21640      */
21641     selectOnFocus : false,
21642     /**
21643      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21644      */
21645     blankText : "This field is required",
21646     /**
21647      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21648      * If available, this function will be called only after the basic validators all return true, and will be passed the
21649      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21650      */
21651     validator : null,
21652     /**
21653      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21654      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21655      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
21656      */
21657     regex : null,
21658     /**
21659      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21660      */
21661     regexText : "",
21662     /**
21663      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
21664      */
21665     emptyText : null,
21666     /**
21667      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
21668      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
21669      */
21670     emptyClass : 'x-form-empty-field',
21671
21672     // private
21673     initEvents : function(){
21674         Roo.form.TextField.superclass.initEvents.call(this);
21675         if(this.validationEvent == 'keyup'){
21676             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21677             this.el.on('keyup', this.filterValidation, this);
21678         }
21679         else if(this.validationEvent !== false){
21680             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21681         }
21682         if(this.selectOnFocus || this.emptyText){
21683             this.on("focus", this.preFocus, this);
21684             if(this.emptyText){
21685                 this.on('blur', this.postBlur, this);
21686                 this.applyEmptyText();
21687             }
21688         }
21689         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21690             this.el.on("keypress", this.filterKeys, this);
21691         }
21692         if(this.grow){
21693             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
21694             this.el.on("click", this.autoSize,  this);
21695         }
21696     },
21697
21698     processValue : function(value){
21699         if(this.stripCharsRe){
21700             var newValue = value.replace(this.stripCharsRe, '');
21701             if(newValue !== value){
21702                 this.setRawValue(newValue);
21703                 return newValue;
21704             }
21705         }
21706         return value;
21707     },
21708
21709     filterValidation : function(e){
21710         if(!e.isNavKeyPress()){
21711             this.validationTask.delay(this.validationDelay);
21712         }
21713     },
21714
21715     // private
21716     onKeyUp : function(e){
21717         if(!e.isNavKeyPress()){
21718             this.autoSize();
21719         }
21720     },
21721
21722     /**
21723      * Resets the current field value to the originally-loaded value and clears any validation messages.
21724      * Also adds emptyText and emptyClass if the original value was blank.
21725      */
21726     reset : function(){
21727         Roo.form.TextField.superclass.reset.call(this);
21728         this.applyEmptyText();
21729     },
21730
21731     applyEmptyText : function(){
21732         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
21733             this.setRawValue(this.emptyText);
21734             this.el.addClass(this.emptyClass);
21735         }
21736     },
21737
21738     // private
21739     preFocus : function(){
21740         if(this.emptyText){
21741             if(this.el.dom.value == this.emptyText){
21742                 this.setRawValue('');
21743             }
21744             this.el.removeClass(this.emptyClass);
21745         }
21746         if(this.selectOnFocus){
21747             this.el.dom.select();
21748         }
21749     },
21750
21751     // private
21752     postBlur : function(){
21753         this.applyEmptyText();
21754     },
21755
21756     // private
21757     filterKeys : function(e){
21758         var k = e.getKey();
21759         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21760             return;
21761         }
21762         var c = e.getCharCode(), cc = String.fromCharCode(c);
21763         if(Roo.isIE && (e.isSpecialKey() || !cc)){
21764             return;
21765         }
21766         if(!this.maskRe.test(cc)){
21767             e.stopEvent();
21768         }
21769     },
21770
21771     setValue : function(v){
21772         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
21773             this.el.removeClass(this.emptyClass);
21774         }
21775         Roo.form.TextField.superclass.setValue.apply(this, arguments);
21776         this.applyEmptyText();
21777         this.autoSize();
21778     },
21779
21780     /**
21781      * Validates a value according to the field's validation rules and marks the field as invalid
21782      * if the validation fails
21783      * @param {Mixed} value The value to validate
21784      * @return {Boolean} True if the value is valid, else false
21785      */
21786     validateValue : function(value){
21787         if(value.length < 1 || value === this.emptyText){ // if it's blank
21788              if(this.allowBlank){
21789                 this.clearInvalid();
21790                 return true;
21791              }else{
21792                 this.markInvalid(this.blankText);
21793                 return false;
21794              }
21795         }
21796         if(value.length < this.minLength){
21797             this.markInvalid(String.format(this.minLengthText, this.minLength));
21798             return false;
21799         }
21800         if(value.length > this.maxLength){
21801             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
21802             return false;
21803         }
21804         if(this.vtype){
21805             var vt = Roo.form.VTypes;
21806             if(!vt[this.vtype](value, this)){
21807                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
21808                 return false;
21809             }
21810         }
21811         if(typeof this.validator == "function"){
21812             var msg = this.validator(value);
21813             if(msg !== true){
21814                 this.markInvalid(msg);
21815                 return false;
21816             }
21817         }
21818         if(this.regex && !this.regex.test(value)){
21819             this.markInvalid(this.regexText);
21820             return false;
21821         }
21822         return true;
21823     },
21824
21825     /**
21826      * Selects text in this field
21827      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
21828      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
21829      */
21830     selectText : function(start, end){
21831         var v = this.getRawValue();
21832         if(v.length > 0){
21833             start = start === undefined ? 0 : start;
21834             end = end === undefined ? v.length : end;
21835             var d = this.el.dom;
21836             if(d.setSelectionRange){
21837                 d.setSelectionRange(start, end);
21838             }else if(d.createTextRange){
21839                 var range = d.createTextRange();
21840                 range.moveStart("character", start);
21841                 range.moveEnd("character", v.length-end);
21842                 range.select();
21843             }
21844         }
21845     },
21846
21847     /**
21848      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
21849      * This only takes effect if grow = true, and fires the autosize event.
21850      */
21851     autoSize : function(){
21852         if(!this.grow || !this.rendered){
21853             return;
21854         }
21855         if(!this.metrics){
21856             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
21857         }
21858         var el = this.el;
21859         var v = el.dom.value;
21860         var d = document.createElement('div');
21861         d.appendChild(document.createTextNode(v));
21862         v = d.innerHTML;
21863         d = null;
21864         v += "&#160;";
21865         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
21866         this.el.setWidth(w);
21867         this.fireEvent("autosize", this, w);
21868     }
21869 });/*
21870  * Based on:
21871  * Ext JS Library 1.1.1
21872  * Copyright(c) 2006-2007, Ext JS, LLC.
21873  *
21874  * Originally Released Under LGPL - original licence link has changed is not relivant.
21875  *
21876  * Fork - LGPL
21877  * <script type="text/javascript">
21878  */
21879  
21880 /**
21881  * @class Roo.form.Hidden
21882  * @extends Roo.form.TextField
21883  * Simple Hidden element used on forms 
21884  * 
21885  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
21886  * 
21887  * @constructor
21888  * Creates a new Hidden form element.
21889  * @param {Object} config Configuration options
21890  */
21891
21892
21893
21894 // easy hidden field...
21895 Roo.form.Hidden = function(config){
21896     Roo.form.Hidden.superclass.constructor.call(this, config);
21897 };
21898   
21899 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
21900     fieldLabel:      '',
21901     inputType:      'hidden',
21902     width:          50,
21903     allowBlank:     true,
21904     labelSeparator: '',
21905     hidden:         true,
21906     itemCls :       'x-form-item-display-none'
21907
21908
21909 });
21910
21911
21912 /*
21913  * Based on:
21914  * Ext JS Library 1.1.1
21915  * Copyright(c) 2006-2007, Ext JS, LLC.
21916  *
21917  * Originally Released Under LGPL - original licence link has changed is not relivant.
21918  *
21919  * Fork - LGPL
21920  * <script type="text/javascript">
21921  */
21922  
21923 /**
21924  * @class Roo.form.TriggerField
21925  * @extends Roo.form.TextField
21926  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
21927  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
21928  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
21929  * for which you can provide a custom implementation.  For example:
21930  * <pre><code>
21931 var trigger = new Roo.form.TriggerField();
21932 trigger.onTriggerClick = myTriggerFn;
21933 trigger.applyTo('my-field');
21934 </code></pre>
21935  *
21936  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
21937  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
21938  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
21939  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
21940  * @constructor
21941  * Create a new TriggerField.
21942  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
21943  * to the base TextField)
21944  */
21945 Roo.form.TriggerField = function(config){
21946     this.mimicing = false;
21947     Roo.form.TriggerField.superclass.constructor.call(this, config);
21948 };
21949
21950 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
21951     /**
21952      * @cfg {String} triggerClass A CSS class to apply to the trigger
21953      */
21954     /**
21955      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21956      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
21957      */
21958     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
21959     /**
21960      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
21961      */
21962     hideTrigger:false,
21963
21964     /** @cfg {Boolean} grow @hide */
21965     /** @cfg {Number} growMin @hide */
21966     /** @cfg {Number} growMax @hide */
21967
21968     /**
21969      * @hide 
21970      * @method
21971      */
21972     autoSize: Roo.emptyFn,
21973     // private
21974     monitorTab : true,
21975     // private
21976     deferHeight : true,
21977
21978     
21979     actionMode : 'wrap',
21980     // private
21981     onResize : function(w, h){
21982         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
21983         if(typeof w == 'number'){
21984             var x = w - this.trigger.getWidth();
21985             this.el.setWidth(this.adjustWidth('input', x));
21986             this.trigger.setStyle('left', x+'px');
21987         }
21988     },
21989
21990     // private
21991     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21992
21993     // private
21994     getResizeEl : function(){
21995         return this.wrap;
21996     },
21997
21998     // private
21999     getPositionEl : function(){
22000         return this.wrap;
22001     },
22002
22003     // private
22004     alignErrorIcon : function(){
22005         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
22006     },
22007
22008     // private
22009     onRender : function(ct, position){
22010         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
22011         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
22012         this.trigger = this.wrap.createChild(this.triggerConfig ||
22013                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
22014         if(this.hideTrigger){
22015             this.trigger.setDisplayed(false);
22016         }
22017         this.initTrigger();
22018         if(!this.width){
22019             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
22020         }
22021     },
22022
22023     // private
22024     initTrigger : function(){
22025         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
22026         this.trigger.addClassOnOver('x-form-trigger-over');
22027         this.trigger.addClassOnClick('x-form-trigger-click');
22028     },
22029
22030     // private
22031     onDestroy : function(){
22032         if(this.trigger){
22033             this.trigger.removeAllListeners();
22034             this.trigger.remove();
22035         }
22036         if(this.wrap){
22037             this.wrap.remove();
22038         }
22039         Roo.form.TriggerField.superclass.onDestroy.call(this);
22040     },
22041
22042     // private
22043     onFocus : function(){
22044         Roo.form.TriggerField.superclass.onFocus.call(this);
22045         if(!this.mimicing){
22046             this.wrap.addClass('x-trigger-wrap-focus');
22047             this.mimicing = true;
22048             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
22049             if(this.monitorTab){
22050                 this.el.on("keydown", this.checkTab, this);
22051             }
22052         }
22053     },
22054
22055     // private
22056     checkTab : function(e){
22057         if(e.getKey() == e.TAB){
22058             this.triggerBlur();
22059         }
22060     },
22061
22062     // private
22063     onBlur : function(){
22064         // do nothing
22065     },
22066
22067     // private
22068     mimicBlur : function(e, t){
22069         if(!this.wrap.contains(t) && this.validateBlur()){
22070             this.triggerBlur();
22071         }
22072     },
22073
22074     // private
22075     triggerBlur : function(){
22076         this.mimicing = false;
22077         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
22078         if(this.monitorTab){
22079             this.el.un("keydown", this.checkTab, this);
22080         }
22081         this.wrap.removeClass('x-trigger-wrap-focus');
22082         Roo.form.TriggerField.superclass.onBlur.call(this);
22083     },
22084
22085     // private
22086     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
22087     validateBlur : function(e, t){
22088         return true;
22089     },
22090
22091     // private
22092     onDisable : function(){
22093         Roo.form.TriggerField.superclass.onDisable.call(this);
22094         if(this.wrap){
22095             this.wrap.addClass('x-item-disabled');
22096         }
22097     },
22098
22099     // private
22100     onEnable : function(){
22101         Roo.form.TriggerField.superclass.onEnable.call(this);
22102         if(this.wrap){
22103             this.wrap.removeClass('x-item-disabled');
22104         }
22105     },
22106
22107     // private
22108     onShow : function(){
22109         var ae = this.getActionEl();
22110         
22111         if(ae){
22112             ae.dom.style.display = '';
22113             ae.dom.style.visibility = 'visible';
22114         }
22115     },
22116
22117     // private
22118     
22119     onHide : function(){
22120         var ae = this.getActionEl();
22121         ae.dom.style.display = 'none';
22122     },
22123
22124     /**
22125      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
22126      * by an implementing function.
22127      * @method
22128      * @param {EventObject} e
22129      */
22130     onTriggerClick : Roo.emptyFn
22131 });
22132
22133 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
22134 // to be extended by an implementing class.  For an example of implementing this class, see the custom
22135 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
22136 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
22137     initComponent : function(){
22138         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
22139
22140         this.triggerConfig = {
22141             tag:'span', cls:'x-form-twin-triggers', cn:[
22142             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
22143             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
22144         ]};
22145     },
22146
22147     getTrigger : function(index){
22148         return this.triggers[index];
22149     },
22150
22151     initTrigger : function(){
22152         var ts = this.trigger.select('.x-form-trigger', true);
22153         this.wrap.setStyle('overflow', 'hidden');
22154         var triggerField = this;
22155         ts.each(function(t, all, index){
22156             t.hide = function(){
22157                 var w = triggerField.wrap.getWidth();
22158                 this.dom.style.display = 'none';
22159                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
22160             };
22161             t.show = function(){
22162                 var w = triggerField.wrap.getWidth();
22163                 this.dom.style.display = '';
22164                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
22165             };
22166             var triggerIndex = 'Trigger'+(index+1);
22167
22168             if(this['hide'+triggerIndex]){
22169                 t.dom.style.display = 'none';
22170             }
22171             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
22172             t.addClassOnOver('x-form-trigger-over');
22173             t.addClassOnClick('x-form-trigger-click');
22174         }, this);
22175         this.triggers = ts.elements;
22176     },
22177
22178     onTrigger1Click : Roo.emptyFn,
22179     onTrigger2Click : Roo.emptyFn
22180 });/*
22181  * Based on:
22182  * Ext JS Library 1.1.1
22183  * Copyright(c) 2006-2007, Ext JS, LLC.
22184  *
22185  * Originally Released Under LGPL - original licence link has changed is not relivant.
22186  *
22187  * Fork - LGPL
22188  * <script type="text/javascript">
22189  */
22190  
22191 /**
22192  * @class Roo.form.TextArea
22193  * @extends Roo.form.TextField
22194  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
22195  * support for auto-sizing.
22196  * @constructor
22197  * Creates a new TextArea
22198  * @param {Object} config Configuration options
22199  */
22200 Roo.form.TextArea = function(config){
22201     Roo.form.TextArea.superclass.constructor.call(this, config);
22202     // these are provided exchanges for backwards compat
22203     // minHeight/maxHeight were replaced by growMin/growMax to be
22204     // compatible with TextField growing config values
22205     if(this.minHeight !== undefined){
22206         this.growMin = this.minHeight;
22207     }
22208     if(this.maxHeight !== undefined){
22209         this.growMax = this.maxHeight;
22210     }
22211 };
22212
22213 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
22214     /**
22215      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
22216      */
22217     growMin : 60,
22218     /**
22219      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
22220      */
22221     growMax: 1000,
22222     /**
22223      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
22224      * in the field (equivalent to setting overflow: hidden, defaults to false)
22225      */
22226     preventScrollbars: false,
22227     /**
22228      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
22229      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
22230      */
22231
22232     // private
22233     onRender : function(ct, position){
22234         if(!this.el){
22235             this.defaultAutoCreate = {
22236                 tag: "textarea",
22237                 style:"width:300px;height:60px;",
22238                 autocomplete: "off"
22239             };
22240         }
22241         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
22242         if(this.grow){
22243             this.textSizeEl = Roo.DomHelper.append(document.body, {
22244                 tag: "pre", cls: "x-form-grow-sizer"
22245             });
22246             if(this.preventScrollbars){
22247                 this.el.setStyle("overflow", "hidden");
22248             }
22249             this.el.setHeight(this.growMin);
22250         }
22251     },
22252
22253     onDestroy : function(){
22254         if(this.textSizeEl){
22255             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
22256         }
22257         Roo.form.TextArea.superclass.onDestroy.call(this);
22258     },
22259
22260     // private
22261     onKeyUp : function(e){
22262         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
22263             this.autoSize();
22264         }
22265     },
22266
22267     /**
22268      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
22269      * This only takes effect if grow = true, and fires the autosize event if the height changes.
22270      */
22271     autoSize : function(){
22272         if(!this.grow || !this.textSizeEl){
22273             return;
22274         }
22275         var el = this.el;
22276         var v = el.dom.value;
22277         var ts = this.textSizeEl;
22278
22279         ts.innerHTML = '';
22280         ts.appendChild(document.createTextNode(v));
22281         v = ts.innerHTML;
22282
22283         Roo.fly(ts).setWidth(this.el.getWidth());
22284         if(v.length < 1){
22285             v = "&#160;&#160;";
22286         }else{
22287             if(Roo.isIE){
22288                 v = v.replace(/\n/g, '<p>&#160;</p>');
22289             }
22290             v += "&#160;\n&#160;";
22291         }
22292         ts.innerHTML = v;
22293         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
22294         if(h != this.lastHeight){
22295             this.lastHeight = h;
22296             this.el.setHeight(h);
22297             this.fireEvent("autosize", this, h);
22298         }
22299     }
22300 });/*
22301  * Based on:
22302  * Ext JS Library 1.1.1
22303  * Copyright(c) 2006-2007, Ext JS, LLC.
22304  *
22305  * Originally Released Under LGPL - original licence link has changed is not relivant.
22306  *
22307  * Fork - LGPL
22308  * <script type="text/javascript">
22309  */
22310  
22311
22312 /**
22313  * @class Roo.form.NumberField
22314  * @extends Roo.form.TextField
22315  * Numeric text field that provides automatic keystroke filtering and numeric validation.
22316  * @constructor
22317  * Creates a new NumberField
22318  * @param {Object} config Configuration options
22319  */
22320 Roo.form.NumberField = function(config){
22321     Roo.form.NumberField.superclass.constructor.call(this, config);
22322 };
22323
22324 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
22325     /**
22326      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
22327      */
22328     fieldClass: "x-form-field x-form-num-field",
22329     /**
22330      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
22331      */
22332     allowDecimals : true,
22333     /**
22334      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
22335      */
22336     decimalSeparator : ".",
22337     /**
22338      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
22339      */
22340     decimalPrecision : 2,
22341     /**
22342      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
22343      */
22344     allowNegative : true,
22345     /**
22346      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
22347      */
22348     minValue : Number.NEGATIVE_INFINITY,
22349     /**
22350      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
22351      */
22352     maxValue : Number.MAX_VALUE,
22353     /**
22354      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
22355      */
22356     minText : "The minimum value for this field is {0}",
22357     /**
22358      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
22359      */
22360     maxText : "The maximum value for this field is {0}",
22361     /**
22362      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
22363      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
22364      */
22365     nanText : "{0} is not a valid number",
22366
22367     // private
22368     initEvents : function(){
22369         Roo.form.NumberField.superclass.initEvents.call(this);
22370         var allowed = "0123456789";
22371         if(this.allowDecimals){
22372             allowed += this.decimalSeparator;
22373         }
22374         if(this.allowNegative){
22375             allowed += "-";
22376         }
22377         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
22378         var keyPress = function(e){
22379             var k = e.getKey();
22380             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
22381                 return;
22382             }
22383             var c = e.getCharCode();
22384             if(allowed.indexOf(String.fromCharCode(c)) === -1){
22385                 e.stopEvent();
22386             }
22387         };
22388         this.el.on("keypress", keyPress, this);
22389     },
22390
22391     // private
22392     validateValue : function(value){
22393         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
22394             return false;
22395         }
22396         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22397              return true;
22398         }
22399         var num = this.parseValue(value);
22400         if(isNaN(num)){
22401             this.markInvalid(String.format(this.nanText, value));
22402             return false;
22403         }
22404         if(num < this.minValue){
22405             this.markInvalid(String.format(this.minText, this.minValue));
22406             return false;
22407         }
22408         if(num > this.maxValue){
22409             this.markInvalid(String.format(this.maxText, this.maxValue));
22410             return false;
22411         }
22412         return true;
22413     },
22414
22415     getValue : function(){
22416         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
22417     },
22418
22419     // private
22420     parseValue : function(value){
22421         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
22422         return isNaN(value) ? '' : value;
22423     },
22424
22425     // private
22426     fixPrecision : function(value){
22427         var nan = isNaN(value);
22428         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
22429             return nan ? '' : value;
22430         }
22431         return parseFloat(value).toFixed(this.decimalPrecision);
22432     },
22433
22434     setValue : function(v){
22435         v = this.fixPrecision(v);
22436         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
22437     },
22438
22439     // private
22440     decimalPrecisionFcn : function(v){
22441         return Math.floor(v);
22442     },
22443
22444     beforeBlur : function(){
22445         var v = this.parseValue(this.getRawValue());
22446         if(v){
22447             this.setValue(v);
22448         }
22449     }
22450 });/*
22451  * Based on:
22452  * Ext JS Library 1.1.1
22453  * Copyright(c) 2006-2007, Ext JS, LLC.
22454  *
22455  * Originally Released Under LGPL - original licence link has changed is not relivant.
22456  *
22457  * Fork - LGPL
22458  * <script type="text/javascript">
22459  */
22460  
22461 /**
22462  * @class Roo.form.DateField
22463  * @extends Roo.form.TriggerField
22464  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22465 * @constructor
22466 * Create a new DateField
22467 * @param {Object} config
22468  */
22469 Roo.form.DateField = function(config){
22470     Roo.form.DateField.superclass.constructor.call(this, config);
22471     
22472       this.addEvents({
22473          
22474         /**
22475          * @event select
22476          * Fires when a date is selected
22477              * @param {Roo.form.DateField} combo This combo box
22478              * @param {Date} date The date selected
22479              */
22480         'select' : true
22481          
22482     });
22483     
22484     
22485     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22486     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22487     this.ddMatch = null;
22488     if(this.disabledDates){
22489         var dd = this.disabledDates;
22490         var re = "(?:";
22491         for(var i = 0; i < dd.length; i++){
22492             re += dd[i];
22493             if(i != dd.length-1) re += "|";
22494         }
22495         this.ddMatch = new RegExp(re + ")");
22496     }
22497 };
22498
22499 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
22500     /**
22501      * @cfg {String} format
22502      * The default date format string which can be overriden for localization support.  The format must be
22503      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22504      */
22505     format : "m/d/y",
22506     /**
22507      * @cfg {String} altFormats
22508      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22509      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22510      */
22511     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22512     /**
22513      * @cfg {Array} disabledDays
22514      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22515      */
22516     disabledDays : null,
22517     /**
22518      * @cfg {String} disabledDaysText
22519      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22520      */
22521     disabledDaysText : "Disabled",
22522     /**
22523      * @cfg {Array} disabledDates
22524      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22525      * expression so they are very powerful. Some examples:
22526      * <ul>
22527      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22528      * <li>["03/08", "09/16"] would disable those days for every year</li>
22529      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22530      * <li>["03/../2006"] would disable every day in March 2006</li>
22531      * <li>["^03"] would disable every day in every March</li>
22532      * </ul>
22533      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22534      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22535      */
22536     disabledDates : null,
22537     /**
22538      * @cfg {String} disabledDatesText
22539      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22540      */
22541     disabledDatesText : "Disabled",
22542     /**
22543      * @cfg {Date/String} minValue
22544      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22545      * valid format (defaults to null).
22546      */
22547     minValue : null,
22548     /**
22549      * @cfg {Date/String} maxValue
22550      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22551      * valid format (defaults to null).
22552      */
22553     maxValue : null,
22554     /**
22555      * @cfg {String} minText
22556      * The error text to display when the date in the cell is before minValue (defaults to
22557      * 'The date in this field must be after {minValue}').
22558      */
22559     minText : "The date in this field must be equal to or after {0}",
22560     /**
22561      * @cfg {String} maxText
22562      * The error text to display when the date in the cell is after maxValue (defaults to
22563      * 'The date in this field must be before {maxValue}').
22564      */
22565     maxText : "The date in this field must be equal to or before {0}",
22566     /**
22567      * @cfg {String} invalidText
22568      * The error text to display when the date in the field is invalid (defaults to
22569      * '{value} is not a valid date - it must be in the format {format}').
22570      */
22571     invalidText : "{0} is not a valid date - it must be in the format {1}",
22572     /**
22573      * @cfg {String} triggerClass
22574      * An additional CSS class used to style the trigger button.  The trigger will always get the
22575      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22576      * which displays a calendar icon).
22577      */
22578     triggerClass : 'x-form-date-trigger',
22579     
22580
22581     /**
22582      * @cfg {bool} useIso
22583      * if enabled, then the date field will use a hidden field to store the 
22584      * real value as iso formated date. default (false)
22585      */ 
22586     useIso : false,
22587     /**
22588      * @cfg {String/Object} autoCreate
22589      * A DomHelper element spec, or true for a default element spec (defaults to
22590      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22591      */ 
22592     // private
22593     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22594     
22595     // private
22596     hiddenField: false,
22597     
22598     onRender : function(ct, position)
22599     {
22600         Roo.form.DateField.superclass.onRender.call(this, ct, position);
22601         if (this.useIso) {
22602             this.el.dom.removeAttribute('name'); 
22603             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22604                     'before', true);
22605             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
22606             // prevent input submission
22607             this.hiddenName = this.name;
22608         }
22609             
22610             
22611     },
22612     
22613     // private
22614     validateValue : function(value)
22615     {
22616         value = this.formatDate(value);
22617         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22618             return false;
22619         }
22620         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22621              return true;
22622         }
22623         var svalue = value;
22624         value = this.parseDate(value);
22625         if(!value){
22626             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22627             return false;
22628         }
22629         var time = value.getTime();
22630         if(this.minValue && time < this.minValue.getTime()){
22631             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22632             return false;
22633         }
22634         if(this.maxValue && time > this.maxValue.getTime()){
22635             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22636             return false;
22637         }
22638         if(this.disabledDays){
22639             var day = value.getDay();
22640             for(var i = 0; i < this.disabledDays.length; i++) {
22641                 if(day === this.disabledDays[i]){
22642                     this.markInvalid(this.disabledDaysText);
22643                     return false;
22644                 }
22645             }
22646         }
22647         var fvalue = this.formatDate(value);
22648         if(this.ddMatch && this.ddMatch.test(fvalue)){
22649             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22650             return false;
22651         }
22652         return true;
22653     },
22654
22655     // private
22656     // Provides logic to override the default TriggerField.validateBlur which just returns true
22657     validateBlur : function(){
22658         return !this.menu || !this.menu.isVisible();
22659     },
22660
22661     /**
22662      * Returns the current date value of the date field.
22663      * @return {Date} The date value
22664      */
22665     getValue : function(){
22666         
22667         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22668     },
22669
22670     /**
22671      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22672      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22673      * (the default format used is "m/d/y").
22674      * <br />Usage:
22675      * <pre><code>
22676 //All of these calls set the same date value (May 4, 2006)
22677
22678 //Pass a date object:
22679 var dt = new Date('5/4/06');
22680 dateField.setValue(dt);
22681
22682 //Pass a date string (default format):
22683 dateField.setValue('5/4/06');
22684
22685 //Pass a date string (custom format):
22686 dateField.format = 'Y-m-d';
22687 dateField.setValue('2006-5-4');
22688 </code></pre>
22689      * @param {String/Date} date The date or valid date string
22690      */
22691     setValue : function(date){
22692         if (this.hiddenField) {
22693             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22694         }
22695         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22696     },
22697
22698     // private
22699     parseDate : function(value){
22700         if(!value || value instanceof Date){
22701             return value;
22702         }
22703         var v = Date.parseDate(value, this.format);
22704         if(!v && this.altFormats){
22705             if(!this.altFormatsArray){
22706                 this.altFormatsArray = this.altFormats.split("|");
22707             }
22708             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22709                 v = Date.parseDate(value, this.altFormatsArray[i]);
22710             }
22711         }
22712         return v;
22713     },
22714
22715     // private
22716     formatDate : function(date, fmt){
22717         return (!date || !(date instanceof Date)) ?
22718                date : date.dateFormat(fmt || this.format);
22719     },
22720
22721     // private
22722     menuListeners : {
22723         select: function(m, d){
22724             this.setValue(d);
22725             this.fireEvent('select', this, d);
22726         },
22727         show : function(){ // retain focus styling
22728             this.onFocus();
22729         },
22730         hide : function(){
22731             this.focus.defer(10, this);
22732             var ml = this.menuListeners;
22733             this.menu.un("select", ml.select,  this);
22734             this.menu.un("show", ml.show,  this);
22735             this.menu.un("hide", ml.hide,  this);
22736         }
22737     },
22738
22739     // private
22740     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22741     onTriggerClick : function(){
22742         if(this.disabled){
22743             return;
22744         }
22745         if(this.menu == null){
22746             this.menu = new Roo.menu.DateMenu();
22747         }
22748         Roo.apply(this.menu.picker,  {
22749             showClear: this.allowBlank,
22750             minDate : this.minValue,
22751             maxDate : this.maxValue,
22752             disabledDatesRE : this.ddMatch,
22753             disabledDatesText : this.disabledDatesText,
22754             disabledDays : this.disabledDays,
22755             disabledDaysText : this.disabledDaysText,
22756             format : this.format,
22757             minText : String.format(this.minText, this.formatDate(this.minValue)),
22758             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22759         });
22760         this.menu.on(Roo.apply({}, this.menuListeners, {
22761             scope:this
22762         }));
22763         this.menu.picker.setValue(this.getValue() || new Date());
22764         this.menu.show(this.el, "tl-bl?");
22765     },
22766
22767     beforeBlur : function(){
22768         var v = this.parseDate(this.getRawValue());
22769         if(v){
22770             this.setValue(v);
22771         }
22772     }
22773
22774     /** @cfg {Boolean} grow @hide */
22775     /** @cfg {Number} growMin @hide */
22776     /** @cfg {Number} growMax @hide */
22777     /**
22778      * @hide
22779      * @method autoSize
22780      */
22781 });/*
22782  * Based on:
22783  * Ext JS Library 1.1.1
22784  * Copyright(c) 2006-2007, Ext JS, LLC.
22785  *
22786  * Originally Released Under LGPL - original licence link has changed is not relivant.
22787  *
22788  * Fork - LGPL
22789  * <script type="text/javascript">
22790  */
22791  
22792
22793 /**
22794  * @class Roo.form.ComboBox
22795  * @extends Roo.form.TriggerField
22796  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
22797  * @constructor
22798  * Create a new ComboBox.
22799  * @param {Object} config Configuration options
22800  */
22801 Roo.form.ComboBox = function(config){
22802     Roo.form.ComboBox.superclass.constructor.call(this, config);
22803     this.addEvents({
22804         /**
22805          * @event expand
22806          * Fires when the dropdown list is expanded
22807              * @param {Roo.form.ComboBox} combo This combo box
22808              */
22809         'expand' : true,
22810         /**
22811          * @event collapse
22812          * Fires when the dropdown list is collapsed
22813              * @param {Roo.form.ComboBox} combo This combo box
22814              */
22815         'collapse' : true,
22816         /**
22817          * @event beforeselect
22818          * Fires before a list item is selected. Return false to cancel the selection.
22819              * @param {Roo.form.ComboBox} combo This combo box
22820              * @param {Roo.data.Record} record The data record returned from the underlying store
22821              * @param {Number} index The index of the selected item in the dropdown list
22822              */
22823         'beforeselect' : true,
22824         /**
22825          * @event select
22826          * Fires when a list item is selected
22827              * @param {Roo.form.ComboBox} combo This combo box
22828              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
22829              * @param {Number} index The index of the selected item in the dropdown list
22830              */
22831         'select' : true,
22832         /**
22833          * @event beforequery
22834          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
22835          * The event object passed has these properties:
22836              * @param {Roo.form.ComboBox} combo This combo box
22837              * @param {String} query The query
22838              * @param {Boolean} forceAll true to force "all" query
22839              * @param {Boolean} cancel true to cancel the query
22840              * @param {Object} e The query event object
22841              */
22842         'beforequery': true,
22843          /**
22844          * @event add
22845          * Fires when the 'add' icon is pressed (add a listener to enable add button)
22846              * @param {Roo.form.ComboBox} combo This combo box
22847              */
22848         'add' : true,
22849         /**
22850          * @event edit
22851          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
22852              * @param {Roo.form.ComboBox} combo This combo box
22853              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
22854              */
22855         'edit' : true
22856         
22857         
22858     });
22859     if(this.transform){
22860         this.allowDomMove = false;
22861         var s = Roo.getDom(this.transform);
22862         if(!this.hiddenName){
22863             this.hiddenName = s.name;
22864         }
22865         if(!this.store){
22866             this.mode = 'local';
22867             var d = [], opts = s.options;
22868             for(var i = 0, len = opts.length;i < len; i++){
22869                 var o = opts[i];
22870                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
22871                 if(o.selected) {
22872                     this.value = value;
22873                 }
22874                 d.push([value, o.text]);
22875             }
22876             this.store = new Roo.data.SimpleStore({
22877                 'id': 0,
22878                 fields: ['value', 'text'],
22879                 data : d
22880             });
22881             this.valueField = 'value';
22882             this.displayField = 'text';
22883         }
22884         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
22885         if(!this.lazyRender){
22886             this.target = true;
22887             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
22888             s.parentNode.removeChild(s); // remove it
22889             this.render(this.el.parentNode);
22890         }else{
22891             s.parentNode.removeChild(s); // remove it
22892         }
22893
22894     }
22895     if (this.store) {
22896         this.store = Roo.factory(this.store, Roo.data);
22897     }
22898     
22899     this.selectedIndex = -1;
22900     if(this.mode == 'local'){
22901         if(config.queryDelay === undefined){
22902             this.queryDelay = 10;
22903         }
22904         if(config.minChars === undefined){
22905             this.minChars = 0;
22906         }
22907     }
22908 };
22909
22910 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
22911     /**
22912      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
22913      */
22914     /**
22915      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
22916      * rendering into an Roo.Editor, defaults to false)
22917      */
22918     /**
22919      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
22920      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
22921      */
22922     /**
22923      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
22924      */
22925     /**
22926      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
22927      * the dropdown list (defaults to undefined, with no header element)
22928      */
22929
22930      /**
22931      * @cfg {String/Roo.Template} tpl The template to use to render the output
22932      */
22933      
22934     // private
22935     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
22936     /**
22937      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
22938      */
22939     listWidth: undefined,
22940     /**
22941      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
22942      * mode = 'remote' or 'text' if mode = 'local')
22943      */
22944     displayField: undefined,
22945     /**
22946      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
22947      * mode = 'remote' or 'value' if mode = 'local'). 
22948      * Note: use of a valueField requires the user make a selection
22949      * in order for a value to be mapped.
22950      */
22951     valueField: undefined,
22952     
22953     
22954     /**
22955      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
22956      * field's data value (defaults to the underlying DOM element's name)
22957      */
22958     hiddenName: undefined,
22959     /**
22960      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
22961      */
22962     listClass: '',
22963     /**
22964      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
22965      */
22966     selectedClass: 'x-combo-selected',
22967     /**
22968      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
22969      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
22970      * which displays a downward arrow icon).
22971      */
22972     triggerClass : 'x-form-arrow-trigger',
22973     /**
22974      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
22975      */
22976     shadow:'sides',
22977     /**
22978      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
22979      * anchor positions (defaults to 'tl-bl')
22980      */
22981     listAlign: 'tl-bl?',
22982     /**
22983      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
22984      */
22985     maxHeight: 300,
22986     /**
22987      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
22988      * query specified by the allQuery config option (defaults to 'query')
22989      */
22990     triggerAction: 'query',
22991     /**
22992      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
22993      * (defaults to 4, does not apply if editable = false)
22994      */
22995     minChars : 4,
22996     /**
22997      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
22998      * delay (typeAheadDelay) if it matches a known value (defaults to false)
22999      */
23000     typeAhead: false,
23001     /**
23002      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
23003      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
23004      */
23005     queryDelay: 500,
23006     /**
23007      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
23008      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
23009      */
23010     pageSize: 0,
23011     /**
23012      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
23013      * when editable = true (defaults to false)
23014      */
23015     selectOnFocus:false,
23016     /**
23017      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
23018      */
23019     queryParam: 'query',
23020     /**
23021      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
23022      * when mode = 'remote' (defaults to 'Loading...')
23023      */
23024     loadingText: 'Loading...',
23025     /**
23026      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
23027      */
23028     resizable: false,
23029     /**
23030      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
23031      */
23032     handleHeight : 8,
23033     /**
23034      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
23035      * traditional select (defaults to true)
23036      */
23037     editable: true,
23038     /**
23039      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
23040      */
23041     allQuery: '',
23042     /**
23043      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
23044      */
23045     mode: 'remote',
23046     /**
23047      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
23048      * listWidth has a higher value)
23049      */
23050     minListWidth : 70,
23051     /**
23052      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
23053      * allow the user to set arbitrary text into the field (defaults to false)
23054      */
23055     forceSelection:false,
23056     /**
23057      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
23058      * if typeAhead = true (defaults to 250)
23059      */
23060     typeAheadDelay : 250,
23061     /**
23062      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
23063      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
23064      */
23065     valueNotFoundText : undefined,
23066     /**
23067      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
23068      */
23069     blockFocus : false,
23070     
23071     /**
23072      * @cfg {Boolean} disableClear Disable showing of clear button.
23073      */
23074     disableClear : false,
23075     /**
23076      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
23077      */
23078     alwaysQuery : false,
23079     
23080     //private
23081     addicon : false,
23082     editicon: false,
23083     
23084     // element that contains real text value.. (when hidden is used..)
23085      
23086     // private
23087     onRender : function(ct, position){
23088         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
23089         if(this.hiddenName){
23090             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
23091                     'before', true);
23092             this.hiddenField.value =
23093                 this.hiddenValue !== undefined ? this.hiddenValue :
23094                 this.value !== undefined ? this.value : '';
23095
23096             // prevent input submission
23097             this.el.dom.removeAttribute('name');
23098              
23099              
23100         }
23101         if(Roo.isGecko){
23102             this.el.dom.setAttribute('autocomplete', 'off');
23103         }
23104
23105         var cls = 'x-combo-list';
23106
23107         this.list = new Roo.Layer({
23108             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
23109         });
23110
23111         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
23112         this.list.setWidth(lw);
23113         this.list.swallowEvent('mousewheel');
23114         this.assetHeight = 0;
23115
23116         if(this.title){
23117             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
23118             this.assetHeight += this.header.getHeight();
23119         }
23120
23121         this.innerList = this.list.createChild({cls:cls+'-inner'});
23122         this.innerList.on('mouseover', this.onViewOver, this);
23123         this.innerList.on('mousemove', this.onViewMove, this);
23124         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23125         
23126         if(this.allowBlank && !this.pageSize && !this.disableClear){
23127             this.footer = this.list.createChild({cls:cls+'-ft'});
23128             this.pageTb = new Roo.Toolbar(this.footer);
23129            
23130         }
23131         if(this.pageSize){
23132             this.footer = this.list.createChild({cls:cls+'-ft'});
23133             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
23134                     {pageSize: this.pageSize});
23135             
23136         }
23137         
23138         if (this.pageTb && this.allowBlank && !this.disableClear) {
23139             var _this = this;
23140             this.pageTb.add(new Roo.Toolbar.Fill(), {
23141                 cls: 'x-btn-icon x-btn-clear',
23142                 text: '&#160;',
23143                 handler: function()
23144                 {
23145                     _this.collapse();
23146                     _this.clearValue();
23147                     _this.onSelect(false, -1);
23148                 }
23149             });
23150         }
23151         if (this.footer) {
23152             this.assetHeight += this.footer.getHeight();
23153         }
23154         
23155
23156         if(!this.tpl){
23157             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
23158         }
23159
23160         this.view = new Roo.View(this.innerList, this.tpl, {
23161             singleSelect:true, store: this.store, selectedClass: this.selectedClass
23162         });
23163
23164         this.view.on('click', this.onViewClick, this);
23165
23166         this.store.on('beforeload', this.onBeforeLoad, this);
23167         this.store.on('load', this.onLoad, this);
23168         this.store.on('loadexception', this.onLoadException, this);
23169
23170         if(this.resizable){
23171             this.resizer = new Roo.Resizable(this.list,  {
23172                pinned:true, handles:'se'
23173             });
23174             this.resizer.on('resize', function(r, w, h){
23175                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
23176                 this.listWidth = w;
23177                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
23178                 this.restrictHeight();
23179             }, this);
23180             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
23181         }
23182         if(!this.editable){
23183             this.editable = true;
23184             this.setEditable(false);
23185         }  
23186         
23187         
23188         if (typeof(this.events.add.listeners) != 'undefined') {
23189             
23190             this.addicon = this.wrap.createChild(
23191                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
23192        
23193             this.addicon.on('click', function(e) {
23194                 this.fireEvent('add', this);
23195             }, this);
23196         }
23197         if (typeof(this.events.edit.listeners) != 'undefined') {
23198             
23199             this.editicon = this.wrap.createChild(
23200                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
23201             if (this.addicon) {
23202                 this.editicon.setStyle('margin-left', '40px');
23203             }
23204             this.editicon.on('click', function(e) {
23205                 
23206                 // we fire even  if inothing is selected..
23207                 this.fireEvent('edit', this, this.lastData );
23208                 
23209             }, this);
23210         }
23211         
23212         
23213         
23214     },
23215
23216     // private
23217     initEvents : function(){
23218         Roo.form.ComboBox.superclass.initEvents.call(this);
23219
23220         this.keyNav = new Roo.KeyNav(this.el, {
23221             "up" : function(e){
23222                 this.inKeyMode = true;
23223                 this.selectPrev();
23224             },
23225
23226             "down" : function(e){
23227                 if(!this.isExpanded()){
23228                     this.onTriggerClick();
23229                 }else{
23230                     this.inKeyMode = true;
23231                     this.selectNext();
23232                 }
23233             },
23234
23235             "enter" : function(e){
23236                 this.onViewClick();
23237                 //return true;
23238             },
23239
23240             "esc" : function(e){
23241                 this.collapse();
23242             },
23243
23244             "tab" : function(e){
23245                 this.onViewClick(false);
23246                 this.fireEvent("specialkey", this, e);
23247                 return true;
23248             },
23249
23250             scope : this,
23251
23252             doRelay : function(foo, bar, hname){
23253                 if(hname == 'down' || this.scope.isExpanded()){
23254                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
23255                 }
23256                 return true;
23257             },
23258
23259             forceKeyDown: true
23260         });
23261         this.queryDelay = Math.max(this.queryDelay || 10,
23262                 this.mode == 'local' ? 10 : 250);
23263         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
23264         if(this.typeAhead){
23265             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
23266         }
23267         if(this.editable !== false){
23268             this.el.on("keyup", this.onKeyUp, this);
23269         }
23270         if(this.forceSelection){
23271             this.on('blur', this.doForce, this);
23272         }
23273     },
23274
23275     onDestroy : function(){
23276         if(this.view){
23277             this.view.setStore(null);
23278             this.view.el.removeAllListeners();
23279             this.view.el.remove();
23280             this.view.purgeListeners();
23281         }
23282         if(this.list){
23283             this.list.destroy();
23284         }
23285         if(this.store){
23286             this.store.un('beforeload', this.onBeforeLoad, this);
23287             this.store.un('load', this.onLoad, this);
23288             this.store.un('loadexception', this.onLoadException, this);
23289         }
23290         Roo.form.ComboBox.superclass.onDestroy.call(this);
23291     },
23292
23293     // private
23294     fireKey : function(e){
23295         if(e.isNavKeyPress() && !this.list.isVisible()){
23296             this.fireEvent("specialkey", this, e);
23297         }
23298     },
23299
23300     // private
23301     onResize: function(w, h){
23302         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
23303         
23304         if(typeof w != 'number'){
23305             // we do not handle it!?!?
23306             return;
23307         }
23308         var tw = this.trigger.getWidth();
23309         tw += this.addicon ? this.addicon.getWidth() : 0;
23310         tw += this.editicon ? this.editicon.getWidth() : 0;
23311         var x = w - tw;
23312         this.el.setWidth( this.adjustWidth('input', x));
23313             
23314         this.trigger.setStyle('left', x+'px');
23315         
23316         if(this.list && this.listWidth === undefined){
23317             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
23318             this.list.setWidth(lw);
23319             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23320         }
23321         
23322     
23323         
23324     },
23325
23326     /**
23327      * Allow or prevent the user from directly editing the field text.  If false is passed,
23328      * the user will only be able to select from the items defined in the dropdown list.  This method
23329      * is the runtime equivalent of setting the 'editable' config option at config time.
23330      * @param {Boolean} value True to allow the user to directly edit the field text
23331      */
23332     setEditable : function(value){
23333         if(value == this.editable){
23334             return;
23335         }
23336         this.editable = value;
23337         if(!value){
23338             this.el.dom.setAttribute('readOnly', true);
23339             this.el.on('mousedown', this.onTriggerClick,  this);
23340             this.el.addClass('x-combo-noedit');
23341         }else{
23342             this.el.dom.setAttribute('readOnly', false);
23343             this.el.un('mousedown', this.onTriggerClick,  this);
23344             this.el.removeClass('x-combo-noedit');
23345         }
23346     },
23347
23348     // private
23349     onBeforeLoad : function(){
23350         if(!this.hasFocus){
23351             return;
23352         }
23353         this.innerList.update(this.loadingText ?
23354                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
23355         this.restrictHeight();
23356         this.selectedIndex = -1;
23357     },
23358
23359     // private
23360     onLoad : function(){
23361         if(!this.hasFocus){
23362             return;
23363         }
23364         if(this.store.getCount() > 0){
23365             this.expand();
23366             this.restrictHeight();
23367             if(this.lastQuery == this.allQuery){
23368                 if(this.editable){
23369                     this.el.dom.select();
23370                 }
23371                 if(!this.selectByValue(this.value, true)){
23372                     this.select(0, true);
23373                 }
23374             }else{
23375                 this.selectNext();
23376                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
23377                     this.taTask.delay(this.typeAheadDelay);
23378                 }
23379             }
23380         }else{
23381             this.onEmptyResults();
23382         }
23383         //this.el.focus();
23384     },
23385     // private
23386     onLoadException : function()
23387     {
23388         this.collapse();
23389         Roo.log(this.store.reader.jsonData);
23390         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
23391             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
23392         }
23393         
23394         
23395     },
23396     // private
23397     onTypeAhead : function(){
23398         if(this.store.getCount() > 0){
23399             var r = this.store.getAt(0);
23400             var newValue = r.data[this.displayField];
23401             var len = newValue.length;
23402             var selStart = this.getRawValue().length;
23403             if(selStart != len){
23404                 this.setRawValue(newValue);
23405                 this.selectText(selStart, newValue.length);
23406             }
23407         }
23408     },
23409
23410     // private
23411     onSelect : function(record, index){
23412         if(this.fireEvent('beforeselect', this, record, index) !== false){
23413             this.setFromData(index > -1 ? record.data : false);
23414             this.collapse();
23415             this.fireEvent('select', this, record, index);
23416         }
23417     },
23418
23419     /**
23420      * Returns the currently selected field value or empty string if no value is set.
23421      * @return {String} value The selected value
23422      */
23423     getValue : function(){
23424         if(this.valueField){
23425             return typeof this.value != 'undefined' ? this.value : '';
23426         }else{
23427             return Roo.form.ComboBox.superclass.getValue.call(this);
23428         }
23429     },
23430
23431     /**
23432      * Clears any text/value currently set in the field
23433      */
23434     clearValue : function(){
23435         if(this.hiddenField){
23436             this.hiddenField.value = '';
23437         }
23438         this.value = '';
23439         this.setRawValue('');
23440         this.lastSelectionText = '';
23441         this.applyEmptyText();
23442     },
23443
23444     /**
23445      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
23446      * will be displayed in the field.  If the value does not match the data value of an existing item,
23447      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
23448      * Otherwise the field will be blank (although the value will still be set).
23449      * @param {String} value The value to match
23450      */
23451     setValue : function(v){
23452         var text = v;
23453         if(this.valueField){
23454             var r = this.findRecord(this.valueField, v);
23455             if(r){
23456                 text = r.data[this.displayField];
23457             }else if(this.valueNotFoundText !== undefined){
23458                 text = this.valueNotFoundText;
23459             }
23460         }
23461         this.lastSelectionText = text;
23462         if(this.hiddenField){
23463             this.hiddenField.value = v;
23464         }
23465         Roo.form.ComboBox.superclass.setValue.call(this, text);
23466         this.value = v;
23467     },
23468     /**
23469      * @property {Object} the last set data for the element
23470      */
23471     
23472     lastData : false,
23473     /**
23474      * Sets the value of the field based on a object which is related to the record format for the store.
23475      * @param {Object} value the value to set as. or false on reset?
23476      */
23477     setFromData : function(o){
23478         var dv = ''; // display value
23479         var vv = ''; // value value..
23480         this.lastData = o;
23481         if (this.displayField) {
23482             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
23483         } else {
23484             // this is an error condition!!!
23485             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
23486         }
23487         
23488         if(this.valueField){
23489             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
23490         }
23491         if(this.hiddenField){
23492             this.hiddenField.value = vv;
23493             
23494             this.lastSelectionText = dv;
23495             Roo.form.ComboBox.superclass.setValue.call(this, dv);
23496             this.value = vv;
23497             return;
23498         }
23499         // no hidden field.. - we store the value in 'value', but still display
23500         // display field!!!!
23501         this.lastSelectionText = dv;
23502         Roo.form.ComboBox.superclass.setValue.call(this, dv);
23503         this.value = vv;
23504         
23505         
23506     },
23507     // private
23508     reset : function(){
23509         // overridden so that last data is reset..
23510         this.setValue(this.originalValue);
23511         this.clearInvalid();
23512         this.lastData = false;
23513     },
23514     // private
23515     findRecord : function(prop, value){
23516         var record;
23517         if(this.store.getCount() > 0){
23518             this.store.each(function(r){
23519                 if(r.data[prop] == value){
23520                     record = r;
23521                     return false;
23522                 }
23523                 return true;
23524             });
23525         }
23526         return record;
23527     },
23528     
23529     getName: function()
23530     {
23531         // returns hidden if it's set..
23532         if (!this.rendered) {return ''};
23533         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
23534         
23535     },
23536     // private
23537     onViewMove : function(e, t){
23538         this.inKeyMode = false;
23539     },
23540
23541     // private
23542     onViewOver : function(e, t){
23543         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
23544             return;
23545         }
23546         var item = this.view.findItemFromChild(t);
23547         if(item){
23548             var index = this.view.indexOf(item);
23549             this.select(index, false);
23550         }
23551     },
23552
23553     // private
23554     onViewClick : function(doFocus)
23555     {
23556         var index = this.view.getSelectedIndexes()[0];
23557         var r = this.store.getAt(index);
23558         if(r){
23559             this.onSelect(r, index);
23560         }
23561         if(doFocus !== false && !this.blockFocus){
23562             this.el.focus();
23563         }
23564     },
23565
23566     // private
23567     restrictHeight : function(){
23568         this.innerList.dom.style.height = '';
23569         var inner = this.innerList.dom;
23570         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
23571         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
23572         this.list.beginUpdate();
23573         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
23574         this.list.alignTo(this.el, this.listAlign);
23575         this.list.endUpdate();
23576     },
23577
23578     // private
23579     onEmptyResults : function(){
23580         this.collapse();
23581     },
23582
23583     /**
23584      * Returns true if the dropdown list is expanded, else false.
23585      */
23586     isExpanded : function(){
23587         return this.list.isVisible();
23588     },
23589
23590     /**
23591      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
23592      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23593      * @param {String} value The data value of the item to select
23594      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23595      * selected item if it is not currently in view (defaults to true)
23596      * @return {Boolean} True if the value matched an item in the list, else false
23597      */
23598     selectByValue : function(v, scrollIntoView){
23599         if(v !== undefined && v !== null){
23600             var r = this.findRecord(this.valueField || this.displayField, v);
23601             if(r){
23602                 this.select(this.store.indexOf(r), scrollIntoView);
23603                 return true;
23604             }
23605         }
23606         return false;
23607     },
23608
23609     /**
23610      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
23611      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23612      * @param {Number} index The zero-based index of the list item to select
23613      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23614      * selected item if it is not currently in view (defaults to true)
23615      */
23616     select : function(index, scrollIntoView){
23617         this.selectedIndex = index;
23618         this.view.select(index);
23619         if(scrollIntoView !== false){
23620             var el = this.view.getNode(index);
23621             if(el){
23622                 this.innerList.scrollChildIntoView(el, false);
23623             }
23624         }
23625     },
23626
23627     // private
23628     selectNext : function(){
23629         var ct = this.store.getCount();
23630         if(ct > 0){
23631             if(this.selectedIndex == -1){
23632                 this.select(0);
23633             }else if(this.selectedIndex < ct-1){
23634                 this.select(this.selectedIndex+1);
23635             }
23636         }
23637     },
23638
23639     // private
23640     selectPrev : function(){
23641         var ct = this.store.getCount();
23642         if(ct > 0){
23643             if(this.selectedIndex == -1){
23644                 this.select(0);
23645             }else if(this.selectedIndex != 0){
23646                 this.select(this.selectedIndex-1);
23647             }
23648         }
23649     },
23650
23651     // private
23652     onKeyUp : function(e){
23653         if(this.editable !== false && !e.isSpecialKey()){
23654             this.lastKey = e.getKey();
23655             this.dqTask.delay(this.queryDelay);
23656         }
23657     },
23658
23659     // private
23660     validateBlur : function(){
23661         return !this.list || !this.list.isVisible();   
23662     },
23663
23664     // private
23665     initQuery : function(){
23666         this.doQuery(this.getRawValue());
23667     },
23668
23669     // private
23670     doForce : function(){
23671         if(this.el.dom.value.length > 0){
23672             this.el.dom.value =
23673                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
23674             this.applyEmptyText();
23675         }
23676     },
23677
23678     /**
23679      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
23680      * query allowing the query action to be canceled if needed.
23681      * @param {String} query The SQL query to execute
23682      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
23683      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
23684      * saved in the current store (defaults to false)
23685      */
23686     doQuery : function(q, forceAll){
23687         if(q === undefined || q === null){
23688             q = '';
23689         }
23690         var qe = {
23691             query: q,
23692             forceAll: forceAll,
23693             combo: this,
23694             cancel:false
23695         };
23696         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
23697             return false;
23698         }
23699         q = qe.query;
23700         forceAll = qe.forceAll;
23701         if(forceAll === true || (q.length >= this.minChars)){
23702             if(this.lastQuery != q || this.alwaysQuery){
23703                 this.lastQuery = q;
23704                 if(this.mode == 'local'){
23705                     this.selectedIndex = -1;
23706                     if(forceAll){
23707                         this.store.clearFilter();
23708                     }else{
23709                         this.store.filter(this.displayField, q);
23710                     }
23711                     this.onLoad();
23712                 }else{
23713                     this.store.baseParams[this.queryParam] = q;
23714                     this.store.load({
23715                         params: this.getParams(q)
23716                     });
23717                     this.expand();
23718                 }
23719             }else{
23720                 this.selectedIndex = -1;
23721                 this.onLoad();   
23722             }
23723         }
23724     },
23725
23726     // private
23727     getParams : function(q){
23728         var p = {};
23729         //p[this.queryParam] = q;
23730         if(this.pageSize){
23731             p.start = 0;
23732             p.limit = this.pageSize;
23733         }
23734         return p;
23735     },
23736
23737     /**
23738      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
23739      */
23740     collapse : function(){
23741         if(!this.isExpanded()){
23742             return;
23743         }
23744         this.list.hide();
23745         Roo.get(document).un('mousedown', this.collapseIf, this);
23746         Roo.get(document).un('mousewheel', this.collapseIf, this);
23747         if (!this.editable) {
23748             Roo.get(document).un('keydown', this.listKeyPress, this);
23749         }
23750         this.fireEvent('collapse', this);
23751     },
23752
23753     // private
23754     collapseIf : function(e){
23755         if(!e.within(this.wrap) && !e.within(this.list)){
23756             this.collapse();
23757         }
23758     },
23759
23760     /**
23761      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
23762      */
23763     expand : function(){
23764         if(this.isExpanded() || !this.hasFocus){
23765             return;
23766         }
23767         this.list.alignTo(this.el, this.listAlign);
23768         this.list.show();
23769         Roo.get(document).on('mousedown', this.collapseIf, this);
23770         Roo.get(document).on('mousewheel', this.collapseIf, this);
23771         if (!this.editable) {
23772             Roo.get(document).on('keydown', this.listKeyPress, this);
23773         }
23774         
23775         this.fireEvent('expand', this);
23776     },
23777
23778     // private
23779     // Implements the default empty TriggerField.onTriggerClick function
23780     onTriggerClick : function(){
23781         if(this.disabled){
23782             return;
23783         }
23784         if(this.isExpanded()){
23785             this.collapse();
23786             if (!this.blockFocus) {
23787                 this.el.focus();
23788             }
23789             
23790         }else {
23791             this.hasFocus = true;
23792             if(this.triggerAction == 'all') {
23793                 this.doQuery(this.allQuery, true);
23794             } else {
23795                 this.doQuery(this.getRawValue());
23796             }
23797             if (!this.blockFocus) {
23798                 this.el.focus();
23799             }
23800         }
23801     },
23802     listKeyPress : function(e)
23803     {
23804         //Roo.log('listkeypress');
23805         // scroll to first matching element based on key pres..
23806         if (e.isSpecialKey()) {
23807             return false;
23808         }
23809         var k = String.fromCharCode(e.getKey()).toUpperCase();
23810         //Roo.log(k);
23811         var match  = false;
23812         var csel = this.view.getSelectedNodes();
23813         var cselitem = false;
23814         if (csel.length) {
23815             var ix = this.view.indexOf(csel[0]);
23816             cselitem  = this.store.getAt(ix);
23817             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
23818                 cselitem = false;
23819             }
23820             
23821         }
23822         
23823         this.store.each(function(v) { 
23824             if (cselitem) {
23825                 // start at existing selection.
23826                 if (cselitem.id == v.id) {
23827                     cselitem = false;
23828                 }
23829                 return;
23830             }
23831                 
23832             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
23833                 match = this.store.indexOf(v);
23834                 return false;
23835             }
23836         }, this);
23837         
23838         if (match === false) {
23839             return true; // no more action?
23840         }
23841         // scroll to?
23842         this.view.select(match);
23843         var sn = Roo.get(this.view.getSelectedNodes()[0])
23844         sn.scrollIntoView(sn.dom.parentNode, false);
23845     }
23846
23847     /** 
23848     * @cfg {Boolean} grow 
23849     * @hide 
23850     */
23851     /** 
23852     * @cfg {Number} growMin 
23853     * @hide 
23854     */
23855     /** 
23856     * @cfg {Number} growMax 
23857     * @hide 
23858     */
23859     /**
23860      * @hide
23861      * @method autoSize
23862      */
23863 });/*
23864  * Based on:
23865  * Ext JS Library 1.1.1
23866  * Copyright(c) 2006-2007, Ext JS, LLC.
23867  *
23868  * Originally Released Under LGPL - original licence link has changed is not relivant.
23869  *
23870  * Fork - LGPL
23871  * <script type="text/javascript">
23872  */
23873 /**
23874  * @class Roo.form.Checkbox
23875  * @extends Roo.form.Field
23876  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
23877  * @constructor
23878  * Creates a new Checkbox
23879  * @param {Object} config Configuration options
23880  */
23881 Roo.form.Checkbox = function(config){
23882     Roo.form.Checkbox.superclass.constructor.call(this, config);
23883     this.addEvents({
23884         /**
23885          * @event check
23886          * Fires when the checkbox is checked or unchecked.
23887              * @param {Roo.form.Checkbox} this This checkbox
23888              * @param {Boolean} checked The new checked value
23889              */
23890         check : true
23891     });
23892 };
23893
23894 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
23895     /**
23896      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
23897      */
23898     focusClass : undefined,
23899     /**
23900      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
23901      */
23902     fieldClass: "x-form-field",
23903     /**
23904      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
23905      */
23906     checked: false,
23907     /**
23908      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
23909      * {tag: "input", type: "checkbox", autocomplete: "off"})
23910      */
23911     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
23912     /**
23913      * @cfg {String} boxLabel The text that appears beside the checkbox
23914      */
23915     boxLabel : "",
23916     /**
23917      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
23918      */  
23919     inputValue : '1',
23920     /**
23921      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23922      */
23923      valueOff: '0', // value when not checked..
23924
23925     actionMode : 'viewEl', 
23926     //
23927     // private
23928     itemCls : 'x-menu-check-item x-form-item',
23929     groupClass : 'x-menu-group-item',
23930     inputType : 'hidden',
23931     
23932     
23933     inSetChecked: false, // check that we are not calling self...
23934     
23935     inputElement: false, // real input element?
23936     basedOn: false, // ????
23937     
23938     isFormField: true, // not sure where this is needed!!!!
23939
23940     onResize : function(){
23941         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
23942         if(!this.boxLabel){
23943             this.el.alignTo(this.wrap, 'c-c');
23944         }
23945     },
23946
23947     initEvents : function(){
23948         Roo.form.Checkbox.superclass.initEvents.call(this);
23949         this.el.on("click", this.onClick,  this);
23950         this.el.on("change", this.onClick,  this);
23951     },
23952
23953
23954     getResizeEl : function(){
23955         return this.wrap;
23956     },
23957
23958     getPositionEl : function(){
23959         return this.wrap;
23960     },
23961
23962     // private
23963     onRender : function(ct, position){
23964         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
23965         /*
23966         if(this.inputValue !== undefined){
23967             this.el.dom.value = this.inputValue;
23968         }
23969         */
23970         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
23971         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
23972         var viewEl = this.wrap.createChild({ 
23973             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
23974         this.viewEl = viewEl;   
23975         this.wrap.on('click', this.onClick,  this); 
23976         
23977         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
23978         this.el.on('propertychange', this.setFromHidden,  this);  //ie
23979         
23980         
23981         
23982         if(this.boxLabel){
23983             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
23984         //    viewEl.on('click', this.onClick,  this); 
23985         }
23986         //if(this.checked){
23987             this.setChecked(this.checked);
23988         //}else{
23989             //this.checked = this.el.dom;
23990         //}
23991
23992     },
23993
23994     // private
23995     initValue : Roo.emptyFn,
23996
23997     /**
23998      * Returns the checked state of the checkbox.
23999      * @return {Boolean} True if checked, else false
24000      */
24001     getValue : function(){
24002         if(this.el){
24003             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
24004         }
24005         return this.valueOff;
24006         
24007     },
24008
24009         // private
24010     onClick : function(){ 
24011         this.setChecked(!this.checked);
24012
24013         //if(this.el.dom.checked != this.checked){
24014         //    this.setValue(this.el.dom.checked);
24015        // }
24016     },
24017
24018     /**
24019      * Sets the checked state of the checkbox.
24020      * On is always based on a string comparison between inputValue and the param.
24021      * @param {Boolean/String} value - the value to set 
24022      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
24023      */
24024     setValue : function(v,suppressEvent){
24025         
24026         
24027         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
24028         //if(this.el && this.el.dom){
24029         //    this.el.dom.checked = this.checked;
24030         //    this.el.dom.defaultChecked = this.checked;
24031         //}
24032         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
24033         //this.fireEvent("check", this, this.checked);
24034     },
24035     // private..
24036     setChecked : function(state,suppressEvent)
24037     {
24038         if (this.inSetChecked) {
24039             this.checked = state;
24040             return;
24041         }
24042         
24043     
24044         if(this.wrap){
24045             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
24046         }
24047         this.checked = state;
24048         if(suppressEvent !== true){
24049             this.fireEvent('check', this, state);
24050         }
24051         this.inSetChecked = true;
24052         this.el.dom.value = state ? this.inputValue : this.valueOff;
24053         this.inSetChecked = false;
24054         
24055     },
24056     // handle setting of hidden value by some other method!!?!?
24057     setFromHidden: function()
24058     {
24059         if(!this.el){
24060             return;
24061         }
24062         //console.log("SET FROM HIDDEN");
24063         //alert('setFrom hidden');
24064         this.setValue(this.el.dom.value);
24065     },
24066     
24067     onDestroy : function()
24068     {
24069         if(this.viewEl){
24070             Roo.get(this.viewEl).remove();
24071         }
24072          
24073         Roo.form.Checkbox.superclass.onDestroy.call(this);
24074     }
24075
24076 });/*
24077  * Based on:
24078  * Ext JS Library 1.1.1
24079  * Copyright(c) 2006-2007, Ext JS, LLC.
24080  *
24081  * Originally Released Under LGPL - original licence link has changed is not relivant.
24082  *
24083  * Fork - LGPL
24084  * <script type="text/javascript">
24085  */
24086  
24087 /**
24088  * @class Roo.form.Radio
24089  * @extends Roo.form.Checkbox
24090  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
24091  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
24092  * @constructor
24093  * Creates a new Radio
24094  * @param {Object} config Configuration options
24095  */
24096 Roo.form.Radio = function(){
24097     Roo.form.Radio.superclass.constructor.apply(this, arguments);
24098 };
24099 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
24100     inputType: 'radio',
24101
24102     /**
24103      * If this radio is part of a group, it will return the selected value
24104      * @return {String}
24105      */
24106     getGroupValue : function(){
24107         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
24108     }
24109 });//<script type="text/javascript">
24110
24111 /*
24112  * Ext JS Library 1.1.1
24113  * Copyright(c) 2006-2007, Ext JS, LLC.
24114  * licensing@extjs.com
24115  * 
24116  * http://www.extjs.com/license
24117  */
24118  
24119  /*
24120   * 
24121   * Known bugs:
24122   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
24123   * - IE ? - no idea how much works there.
24124   * 
24125   * 
24126   * 
24127   */
24128  
24129
24130 /**
24131  * @class Ext.form.HtmlEditor
24132  * @extends Ext.form.Field
24133  * Provides a lightweight HTML Editor component.
24134  * WARNING - THIS CURRENTlY ONLY WORKS ON FIREFOX - USE FCKeditor for a cross platform version
24135  * 
24136  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
24137  * supported by this editor.</b><br/><br/>
24138  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
24139  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24140  */
24141 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
24142       /**
24143      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
24144      */
24145     toolbars : false,
24146     /**
24147      * @cfg {String} createLinkText The default text for the create link prompt
24148      */
24149     createLinkText : 'Please enter the URL for the link:',
24150     /**
24151      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
24152      */
24153     defaultLinkValue : 'http:/'+'/',
24154    
24155      /**
24156      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24157      *                        Roo.resizable.
24158      */
24159     resizable : false,
24160      /**
24161      * @cfg {Number} height (in pixels)
24162      */   
24163     height: 300,
24164    /**
24165      * @cfg {Number} width (in pixels)
24166      */   
24167     width: 500,
24168     
24169     /**
24170      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24171      * 
24172      */
24173     stylesheets: false,
24174     
24175     // id of frame..
24176     frameId: false,
24177     
24178     // private properties
24179     validationEvent : false,
24180     deferHeight: true,
24181     initialized : false,
24182     activated : false,
24183     sourceEditMode : false,
24184     onFocus : Roo.emptyFn,
24185     iframePad:3,
24186     hideMode:'offsets',
24187     
24188     defaultAutoCreate : { // modified by initCompnoent..
24189         tag: "textarea",
24190         style:"width:500px;height:300px;",
24191         autocomplete: "off"
24192     },
24193
24194     // private
24195     initComponent : function(){
24196         this.addEvents({
24197             /**
24198              * @event initialize
24199              * Fires when the editor is fully initialized (including the iframe)
24200              * @param {HtmlEditor} this
24201              */
24202             initialize: true,
24203             /**
24204              * @event activate
24205              * Fires when the editor is first receives the focus. Any insertion must wait
24206              * until after this event.
24207              * @param {HtmlEditor} this
24208              */
24209             activate: true,
24210              /**
24211              * @event beforesync
24212              * Fires before the textarea is updated with content from the editor iframe. Return false
24213              * to cancel the sync.
24214              * @param {HtmlEditor} this
24215              * @param {String} html
24216              */
24217             beforesync: true,
24218              /**
24219              * @event beforepush
24220              * Fires before the iframe editor is updated with content from the textarea. Return false
24221              * to cancel the push.
24222              * @param {HtmlEditor} this
24223              * @param {String} html
24224              */
24225             beforepush: true,
24226              /**
24227              * @event sync
24228              * Fires when the textarea is updated with content from the editor iframe.
24229              * @param {HtmlEditor} this
24230              * @param {String} html
24231              */
24232             sync: true,
24233              /**
24234              * @event push
24235              * Fires when the iframe editor is updated with content from the textarea.
24236              * @param {HtmlEditor} this
24237              * @param {String} html
24238              */
24239             push: true,
24240              /**
24241              * @event editmodechange
24242              * Fires when the editor switches edit modes
24243              * @param {HtmlEditor} this
24244              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
24245              */
24246             editmodechange: true,
24247             /**
24248              * @event editorevent
24249              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24250              * @param {HtmlEditor} this
24251              */
24252             editorevent: true
24253         });
24254         this.defaultAutoCreate =  {
24255             tag: "textarea",
24256             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
24257             autocomplete: "off"
24258         };
24259     },
24260
24261     /**
24262      * Protected method that will not generally be called directly. It
24263      * is called when the editor creates its toolbar. Override this method if you need to
24264      * add custom toolbar buttons.
24265      * @param {HtmlEditor} editor
24266      */
24267     createToolbar : function(editor){
24268         if (!editor.toolbars || !editor.toolbars.length) {
24269             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
24270         }
24271         
24272         for (var i =0 ; i < editor.toolbars.length;i++) {
24273             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
24274             editor.toolbars[i].init(editor);
24275         }
24276          
24277         
24278     },
24279
24280     /**
24281      * Protected method that will not generally be called directly. It
24282      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24283      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24284      */
24285     getDocMarkup : function(){
24286         // body styles..
24287         var st = '';
24288         if (this.stylesheets === false) {
24289             
24290             Roo.get(document.head).select('style').each(function(node) {
24291                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24292             });
24293             
24294             Roo.get(document.head).select('link').each(function(node) { 
24295                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24296             });
24297             
24298         } else if (!this.stylesheets.length) {
24299                 // simple..
24300                 st = '<style type="text/css">' +
24301                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24302                    '</style>';
24303         } else {
24304             Roo.each(this.stylesheets, function(s) {
24305                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
24306             });
24307             
24308         }
24309         
24310         return '<html><head>' + st  +
24311             //<style type="text/css">' +
24312             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24313             //'</style>' +
24314             ' </head><body></body></html>';
24315     },
24316
24317     // private
24318     onRender : function(ct, position)
24319     {
24320         var _t = this;
24321         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
24322         this.el.dom.style.border = '0 none';
24323         this.el.dom.setAttribute('tabIndex', -1);
24324         this.el.addClass('x-hidden');
24325         if(Roo.isIE){ // fix IE 1px bogus margin
24326             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24327         }
24328         this.wrap = this.el.wrap({
24329             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
24330         });
24331         
24332         if (this.resizable) {
24333             this.resizeEl = new Roo.Resizable(this.wrap, {
24334                 pinned : true,
24335                 wrap: true,
24336                 dynamic : true,
24337                 minHeight : this.height,
24338                 height: this.height,
24339                 handles : this.resizable,
24340                 width: this.width,
24341                 listeners : {
24342                     resize : function(r, w, h) {
24343                         _t.onResize(w,h); // -something
24344                     }
24345                 }
24346             });
24347             
24348         }
24349
24350         this.frameId = Roo.id();
24351         
24352         this.createToolbar(this);
24353         
24354       
24355         
24356         var iframe = this.wrap.createChild({
24357             tag: 'iframe',
24358             id: this.frameId,
24359             name: this.frameId,
24360             frameBorder : 'no',
24361             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24362         }, this.el
24363         );
24364         
24365        // console.log(iframe);
24366         //this.wrap.dom.appendChild(iframe);
24367
24368         this.iframe = iframe.dom;
24369
24370          this.assignDocWin();
24371         
24372         this.doc.designMode = 'on';
24373        
24374         this.doc.open();
24375         this.doc.write(this.getDocMarkup());
24376         this.doc.close();
24377
24378         
24379         var task = { // must defer to wait for browser to be ready
24380             run : function(){
24381                 //console.log("run task?" + this.doc.readyState);
24382                 this.assignDocWin();
24383                 if(this.doc.body || this.doc.readyState == 'complete'){
24384                     try {
24385                         this.doc.designMode="on";
24386                     } catch (e) {
24387                         return;
24388                     }
24389                     Roo.TaskMgr.stop(task);
24390                     this.initEditor.defer(10, this);
24391                 }
24392             },
24393             interval : 10,
24394             duration:10000,
24395             scope: this
24396         };
24397         Roo.TaskMgr.start(task);
24398
24399         if(!this.width){
24400             this.setSize(this.wrap.getSize());
24401         }
24402         if (this.resizeEl) {
24403             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
24404             // should trigger onReize..
24405         }
24406     },
24407
24408     // private
24409     onResize : function(w, h)
24410     {
24411         //Roo.log('resize: ' +w + ',' + h );
24412         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
24413         if(this.el && this.iframe){
24414             if(typeof w == 'number'){
24415                 var aw = w - this.wrap.getFrameWidth('lr');
24416                 this.el.setWidth(this.adjustWidth('textarea', aw));
24417                 this.iframe.style.width = aw + 'px';
24418             }
24419             if(typeof h == 'number'){
24420                 var tbh = 0;
24421                 for (var i =0; i < this.toolbars.length;i++) {
24422                     // fixme - ask toolbars for heights?
24423                     tbh += this.toolbars[i].tb.el.getHeight();
24424                     if (this.toolbars[i].footer) {
24425                         tbh += this.toolbars[i].footer.el.getHeight();
24426                     }
24427                 }
24428                 
24429                 
24430                 
24431                 
24432                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
24433                 ah -= 5; // knock a few pixes off for look..
24434                 this.el.setHeight(this.adjustWidth('textarea', ah));
24435                 this.iframe.style.height = ah + 'px';
24436                 if(this.doc){
24437                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
24438                 }
24439             }
24440         }
24441     },
24442
24443     /**
24444      * Toggles the editor between standard and source edit mode.
24445      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24446      */
24447     toggleSourceEdit : function(sourceEditMode){
24448         
24449         this.sourceEditMode = sourceEditMode === true;
24450         
24451         if(this.sourceEditMode){
24452           
24453             this.syncValue();
24454             this.iframe.className = 'x-hidden';
24455             this.el.removeClass('x-hidden');
24456             this.el.dom.removeAttribute('tabIndex');
24457             this.el.focus();
24458         }else{
24459              
24460             this.pushValue();
24461             this.iframe.className = '';
24462             this.el.addClass('x-hidden');
24463             this.el.dom.setAttribute('tabIndex', -1);
24464             this.deferFocus();
24465         }
24466         this.setSize(this.wrap.getSize());
24467         this.fireEvent('editmodechange', this, this.sourceEditMode);
24468     },
24469
24470     // private used internally
24471     createLink : function(){
24472         var url = prompt(this.createLinkText, this.defaultLinkValue);
24473         if(url && url != 'http:/'+'/'){
24474             this.relayCmd('createlink', url);
24475         }
24476     },
24477
24478     // private (for BoxComponent)
24479     adjustSize : Roo.BoxComponent.prototype.adjustSize,
24480
24481     // private (for BoxComponent)
24482     getResizeEl : function(){
24483         return this.wrap;
24484     },
24485
24486     // private (for BoxComponent)
24487     getPositionEl : function(){
24488         return this.wrap;
24489     },
24490
24491     // private
24492     initEvents : function(){
24493         this.originalValue = this.getValue();
24494     },
24495
24496     /**
24497      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24498      * @method
24499      */
24500     markInvalid : Roo.emptyFn,
24501     /**
24502      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24503      * @method
24504      */
24505     clearInvalid : Roo.emptyFn,
24506
24507     setValue : function(v){
24508         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
24509         this.pushValue();
24510     },
24511
24512     /**
24513      * Protected method that will not generally be called directly. If you need/want
24514      * custom HTML cleanup, this is the method you should override.
24515      * @param {String} html The HTML to be cleaned
24516      * return {String} The cleaned HTML
24517      */
24518     cleanHtml : function(html){
24519         html = String(html);
24520         if(html.length > 5){
24521             if(Roo.isSafari){ // strip safari nonsense
24522                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24523             }
24524         }
24525         if(html == '&nbsp;'){
24526             html = '';
24527         }
24528         return html;
24529     },
24530
24531     /**
24532      * Protected method that will not generally be called directly. Syncs the contents
24533      * of the editor iframe with the textarea.
24534      */
24535     syncValue : function(){
24536         if(this.initialized){
24537             var bd = (this.doc.body || this.doc.documentElement);
24538             //this.cleanUpPaste();
24539             var html = bd.innerHTML;
24540             if(Roo.isSafari){
24541                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24542                 var m = bs.match(/text-align:(.*?);/i);
24543                 if(m && m[1]){
24544                     html = '<div style="'+m[0]+'">' + html + '</div>';
24545                 }
24546             }
24547             html = this.cleanHtml(html);
24548             if(this.fireEvent('beforesync', this, html) !== false){
24549                 this.el.dom.value = html;
24550                 this.fireEvent('sync', this, html);
24551             }
24552         }
24553     },
24554
24555     /**
24556      * Protected method that will not generally be called directly. Pushes the value of the textarea
24557      * into the iframe editor.
24558      */
24559     pushValue : function(){
24560         if(this.initialized){
24561             var v = this.el.dom.value;
24562             if(v.length < 1){
24563                 v = '&#160;';
24564             }
24565             
24566             if(this.fireEvent('beforepush', this, v) !== false){
24567                 var d = (this.doc.body || this.doc.documentElement);
24568                 d.innerHTML = v;
24569                 this.cleanUpPaste();
24570                 this.el.dom.value = d.innerHTML;
24571                 this.fireEvent('push', this, v);
24572             }
24573         }
24574     },
24575
24576     // private
24577     deferFocus : function(){
24578         this.focus.defer(10, this);
24579     },
24580
24581     // doc'ed in Field
24582     focus : function(){
24583         if(this.win && !this.sourceEditMode){
24584             this.win.focus();
24585         }else{
24586             this.el.focus();
24587         }
24588     },
24589     
24590     assignDocWin: function()
24591     {
24592         var iframe = this.iframe;
24593         
24594          if(Roo.isIE){
24595             this.doc = iframe.contentWindow.document;
24596             this.win = iframe.contentWindow;
24597         } else {
24598             if (!Roo.get(this.frameId)) {
24599                 return;
24600             }
24601             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24602             this.win = Roo.get(this.frameId).dom.contentWindow;
24603         }
24604     },
24605     
24606     // private
24607     initEditor : function(){
24608         //console.log("INIT EDITOR");
24609         this.assignDocWin();
24610         
24611         
24612         
24613         this.doc.designMode="on";
24614         this.doc.open();
24615         this.doc.write(this.getDocMarkup());
24616         this.doc.close();
24617         
24618         var dbody = (this.doc.body || this.doc.documentElement);
24619         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24620         // this copies styles from the containing element into thsi one..
24621         // not sure why we need all of this..
24622         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24623         ss['background-attachment'] = 'fixed'; // w3c
24624         dbody.bgProperties = 'fixed'; // ie
24625         Roo.DomHelper.applyStyles(dbody, ss);
24626         Roo.EventManager.on(this.doc, {
24627             //'mousedown': this.onEditorEvent,
24628             'mouseup': this.onEditorEvent,
24629             'dblclick': this.onEditorEvent,
24630             'click': this.onEditorEvent,
24631             'keyup': this.onEditorEvent,
24632             buffer:100,
24633             scope: this
24634         });
24635         if(Roo.isGecko){
24636             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24637         }
24638         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24639             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24640         }
24641         this.initialized = true;
24642
24643         this.fireEvent('initialize', this);
24644         this.pushValue();
24645     },
24646
24647     // private
24648     onDestroy : function(){
24649         
24650         
24651         
24652         if(this.rendered){
24653             
24654             for (var i =0; i < this.toolbars.length;i++) {
24655                 // fixme - ask toolbars for heights?
24656                 this.toolbars[i].onDestroy();
24657             }
24658             
24659             this.wrap.dom.innerHTML = '';
24660             this.wrap.remove();
24661         }
24662     },
24663
24664     // private
24665     onFirstFocus : function(){
24666         
24667         this.assignDocWin();
24668         
24669         
24670         this.activated = true;
24671         for (var i =0; i < this.toolbars.length;i++) {
24672             this.toolbars[i].onFirstFocus();
24673         }
24674        
24675         if(Roo.isGecko){ // prevent silly gecko errors
24676             this.win.focus();
24677             var s = this.win.getSelection();
24678             if(!s.focusNode || s.focusNode.nodeType != 3){
24679                 var r = s.getRangeAt(0);
24680                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24681                 r.collapse(true);
24682                 this.deferFocus();
24683             }
24684             try{
24685                 this.execCmd('useCSS', true);
24686                 this.execCmd('styleWithCSS', false);
24687             }catch(e){}
24688         }
24689         this.fireEvent('activate', this);
24690     },
24691
24692     // private
24693     adjustFont: function(btn){
24694         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24695         //if(Roo.isSafari){ // safari
24696         //    adjust *= 2;
24697        // }
24698         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24699         if(Roo.isSafari){ // safari
24700             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24701             v =  (v < 10) ? 10 : v;
24702             v =  (v > 48) ? 48 : v;
24703             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24704             
24705         }
24706         
24707         
24708         v = Math.max(1, v+adjust);
24709         
24710         this.execCmd('FontSize', v  );
24711     },
24712
24713     onEditorEvent : function(e){
24714         this.fireEvent('editorevent', this, e);
24715       //  this.updateToolbar();
24716         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24717     },
24718
24719     insertTag : function(tg)
24720     {
24721         // could be a bit smarter... -> wrap the current selected tRoo..
24722         
24723         this.execCmd("formatblock",   tg);
24724         
24725     },
24726     
24727     insertText : function(txt)
24728     {
24729         
24730         
24731         range = this.createRange();
24732         range.deleteContents();
24733                //alert(Sender.getAttribute('label'));
24734                
24735         range.insertNode(this.doc.createTextNode(txt));
24736     } ,
24737     
24738     // private
24739     relayBtnCmd : function(btn){
24740         this.relayCmd(btn.cmd);
24741     },
24742
24743     /**
24744      * Executes a Midas editor command on the editor document and performs necessary focus and
24745      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24746      * @param {String} cmd The Midas command
24747      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24748      */
24749     relayCmd : function(cmd, value){
24750         this.win.focus();
24751         this.execCmd(cmd, value);
24752         this.fireEvent('editorevent', this);
24753         //this.updateToolbar();
24754         this.deferFocus();
24755     },
24756
24757     /**
24758      * Executes a Midas editor command directly on the editor document.
24759      * For visual commands, you should use {@link #relayCmd} instead.
24760      * <b>This should only be called after the editor is initialized.</b>
24761      * @param {String} cmd The Midas command
24762      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24763      */
24764     execCmd : function(cmd, value){
24765         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24766         this.syncValue();
24767     },
24768
24769    
24770     /**
24771      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24772      * to insert tRoo.
24773      * @param {String} text
24774      */
24775     insertAtCursor : function(text){
24776         if(!this.activated){
24777             return;
24778         }
24779         if(Roo.isIE){
24780             this.win.focus();
24781             var r = this.doc.selection.createRange();
24782             if(r){
24783                 r.collapse(true);
24784                 r.pasteHTML(text);
24785                 this.syncValue();
24786                 this.deferFocus();
24787             }
24788         }else if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24789             this.win.focus();
24790             this.execCmd('InsertHTML', text);
24791             this.deferFocus();
24792         }
24793     },
24794  // private
24795     mozKeyPress : function(e){
24796         if(e.ctrlKey){
24797             var c = e.getCharCode(), cmd;
24798           
24799             if(c > 0){
24800                 c = String.fromCharCode(c).toLowerCase();
24801                 switch(c){
24802                     case 'b':
24803                         cmd = 'bold';
24804                     break;
24805                     case 'i':
24806                         cmd = 'italic';
24807                     break;
24808                     case 'u':
24809                         cmd = 'underline';
24810                         break;
24811                     case 'v':
24812                         this.cleanUpPaste.defer(100, this);
24813                         return;
24814                     break;
24815                 }
24816                 if(cmd){
24817                     this.win.focus();
24818                     this.execCmd(cmd);
24819                     this.deferFocus();
24820                     e.preventDefault();
24821                 }
24822                 
24823             }
24824         }
24825     },
24826
24827     // private
24828     fixKeys : function(){ // load time branching for fastest keydown performance
24829         if(Roo.isIE){
24830             return function(e){
24831                 var k = e.getKey(), r;
24832                 if(k == e.TAB){
24833                     e.stopEvent();
24834                     r = this.doc.selection.createRange();
24835                     if(r){
24836                         r.collapse(true);
24837                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24838                         this.deferFocus();
24839                     }
24840                     return;
24841                 }
24842                 
24843                 if(k == e.ENTER){
24844                     r = this.doc.selection.createRange();
24845                     if(r){
24846                         var target = r.parentElement();
24847                         if(!target || target.tagName.toLowerCase() != 'li'){
24848                             e.stopEvent();
24849                             r.pasteHTML('<br />');
24850                             r.collapse(false);
24851                             r.select();
24852                         }
24853                     }
24854                 }
24855                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24856                     this.cleanUpPaste.defer(100, this);
24857                     return;
24858                 }
24859                 
24860                 
24861             };
24862         }else if(Roo.isOpera){
24863             return function(e){
24864                 var k = e.getKey();
24865                 if(k == e.TAB){
24866                     e.stopEvent();
24867                     this.win.focus();
24868                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24869                     this.deferFocus();
24870                 }
24871                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24872                     this.cleanUpPaste.defer(100, this);
24873                     return;
24874                 }
24875                 
24876             };
24877         }else if(Roo.isSafari){
24878             return function(e){
24879                 var k = e.getKey();
24880                 
24881                 if(k == e.TAB){
24882                     e.stopEvent();
24883                     this.execCmd('InsertText','\t');
24884                     this.deferFocus();
24885                     return;
24886                 }
24887                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24888                     this.cleanUpPaste.defer(100, this);
24889                     return;
24890                 }
24891                 
24892              };
24893         }
24894     }(),
24895     
24896     getAllAncestors: function()
24897     {
24898         var p = this.getSelectedNode();
24899         var a = [];
24900         if (!p) {
24901             a.push(p); // push blank onto stack..
24902             p = this.getParentElement();
24903         }
24904         
24905         
24906         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24907             a.push(p);
24908             p = p.parentNode;
24909         }
24910         a.push(this.doc.body);
24911         return a;
24912     },
24913     lastSel : false,
24914     lastSelNode : false,
24915     
24916     
24917     getSelection : function() 
24918     {
24919         this.assignDocWin();
24920         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24921     },
24922     
24923     getSelectedNode: function() 
24924     {
24925         // this may only work on Gecko!!!
24926         
24927         // should we cache this!!!!
24928         
24929         
24930         
24931          
24932         var range = this.createRange(this.getSelection()).cloneRange();
24933         
24934         if (Roo.isIE) {
24935             var parent = range.parentElement();
24936             while (true) {
24937                 var testRange = range.duplicate();
24938                 testRange.moveToElementText(parent);
24939                 if (testRange.inRange(range)) {
24940                     break;
24941                 }
24942                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24943                     break;
24944                 }
24945                 parent = parent.parentElement;
24946             }
24947             return parent;
24948         }
24949         
24950         // is ancestor a text element.
24951         var ac =  range.commonAncestorContainer;
24952         if (ac.nodeType == 3) {
24953             ac = ac.parentNode;
24954         }
24955         
24956         var ar = ac.childNodes;
24957          
24958         var nodes = [];
24959         var other_nodes = [];
24960         var has_other_nodes = false;
24961         for (var i=0;i<ar.length;i++) {
24962             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24963                 continue;
24964             }
24965             // fullly contained node.
24966             
24967             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24968                 nodes.push(ar[i]);
24969                 continue;
24970             }
24971             
24972             // probably selected..
24973             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24974                 other_nodes.push(ar[i]);
24975                 continue;
24976             }
24977             // outer..
24978             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24979                 continue;
24980             }
24981             
24982             
24983             has_other_nodes = true;
24984         }
24985         if (!nodes.length && other_nodes.length) {
24986             nodes= other_nodes;
24987         }
24988         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24989             return false;
24990         }
24991         
24992         return nodes[0];
24993     },
24994     createRange: function(sel)
24995     {
24996         // this has strange effects when using with 
24997         // top toolbar - not sure if it's a great idea.
24998         //this.editor.contentWindow.focus();
24999         if (typeof sel != "undefined") {
25000             try {
25001                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25002             } catch(e) {
25003                 return this.doc.createRange();
25004             }
25005         } else {
25006             return this.doc.createRange();
25007         }
25008     },
25009     getParentElement: function()
25010     {
25011         
25012         this.assignDocWin();
25013         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25014         
25015         var range = this.createRange(sel);
25016          
25017         try {
25018             var p = range.commonAncestorContainer;
25019             while (p.nodeType == 3) { // text node
25020                 p = p.parentNode;
25021             }
25022             return p;
25023         } catch (e) {
25024             return null;
25025         }
25026     
25027     },
25028     /***
25029      *
25030      * Range intersection.. the hard stuff...
25031      *  '-1' = before
25032      *  '0' = hits..
25033      *  '1' = after.
25034      *         [ -- selected range --- ]
25035      *   [fail]                        [fail]
25036      *
25037      *    basically..
25038      *      if end is before start or  hits it. fail.
25039      *      if start is after end or hits it fail.
25040      *
25041      *   if either hits (but other is outside. - then it's not 
25042      *   
25043      *    
25044      **/
25045     
25046     
25047     // @see http://www.thismuchiknow.co.uk/?p=64.
25048     rangeIntersectsNode : function(range, node)
25049     {
25050         var nodeRange = node.ownerDocument.createRange();
25051         try {
25052             nodeRange.selectNode(node);
25053         } catch (e) {
25054             nodeRange.selectNodeContents(node);
25055         }
25056     
25057         var rangeStartRange = range.cloneRange();
25058         rangeStartRange.collapse(true);
25059     
25060         var rangeEndRange = range.cloneRange();
25061         rangeEndRange.collapse(false);
25062     
25063         var nodeStartRange = nodeRange.cloneRange();
25064         nodeStartRange.collapse(true);
25065     
25066         var nodeEndRange = nodeRange.cloneRange();
25067         nodeEndRange.collapse(false);
25068     
25069         return rangeStartRange.compareBoundaryPoints(
25070                  Range.START_TO_START, nodeEndRange) == -1 &&
25071                rangeEndRange.compareBoundaryPoints(
25072                  Range.START_TO_START, nodeStartRange) == 1;
25073         
25074          
25075     },
25076     rangeCompareNode : function(range, node)
25077     {
25078         var nodeRange = node.ownerDocument.createRange();
25079         try {
25080             nodeRange.selectNode(node);
25081         } catch (e) {
25082             nodeRange.selectNodeContents(node);
25083         }
25084         
25085         
25086         range.collapse(true);
25087     
25088         nodeRange.collapse(true);
25089      
25090         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25091         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
25092          
25093         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25094         
25095         var nodeIsBefore   =  ss == 1;
25096         var nodeIsAfter    = ee == -1;
25097         
25098         if (nodeIsBefore && nodeIsAfter)
25099             return 0; // outer
25100         if (!nodeIsBefore && nodeIsAfter)
25101             return 1; //right trailed.
25102         
25103         if (nodeIsBefore && !nodeIsAfter)
25104             return 2;  // left trailed.
25105         // fully contined.
25106         return 3;
25107     },
25108
25109     // private? - in a new class?
25110     cleanUpPaste :  function()
25111     {
25112         // cleans up the whole document..
25113          Roo.log('cleanuppaste');
25114         this.cleanUpChildren(this.doc.body);
25115         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25116         if (clean != this.doc.body.innerHTML) {
25117             this.doc.body.innerHTML = clean;
25118         }
25119         
25120     },
25121     
25122     cleanWordChars : function(input) {
25123         var he = Roo.form.HtmlEditor;
25124     
25125         var output = input;
25126         Roo.each(he.swapCodes, function(sw) { 
25127         
25128             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25129             output = output.replace(swapper, sw[1]);
25130         });
25131         return output;
25132     },
25133     
25134     
25135     cleanUpChildren : function (n)
25136     {
25137         if (!n.childNodes.length) {
25138             return;
25139         }
25140         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25141            this.cleanUpChild(n.childNodes[i]);
25142         }
25143     },
25144     
25145     
25146         
25147     
25148     cleanUpChild : function (node)
25149     {
25150         //console.log(node);
25151         if (node.nodeName == "#text") {
25152             // clean up silly Windows -- stuff?
25153             return; 
25154         }
25155         if (node.nodeName == "#comment") {
25156             node.parentNode.removeChild(node);
25157             // clean up silly Windows -- stuff?
25158             return; 
25159         }
25160         
25161         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
25162             // remove node.
25163             node.parentNode.removeChild(node);
25164             return;
25165             
25166         }
25167         
25168         var remove_keep_children= Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1;
25169         
25170         // remove <a name=....> as rendering on yahoo mailer is bored with this.
25171         
25172         if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25173             remove_keep_children = true;
25174         }
25175         
25176         if (remove_keep_children) {
25177             this.cleanUpChildren(node);
25178             // inserts everything just before this node...
25179             while (node.childNodes.length) {
25180                 var cn = node.childNodes[0];
25181                 node.removeChild(cn);
25182                 node.parentNode.insertBefore(cn, node);
25183             }
25184             node.parentNode.removeChild(node);
25185             return;
25186         }
25187         
25188         if (!node.attributes || !node.attributes.length) {
25189             this.cleanUpChildren(node);
25190             return;
25191         }
25192         
25193         function cleanAttr(n,v)
25194         {
25195             
25196             if (v.match(/^\./) || v.match(/^\//)) {
25197                 return;
25198             }
25199             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
25200                 return;
25201             }
25202             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
25203             node.removeAttribute(n);
25204             
25205         }
25206         
25207         function cleanStyle(n,v)
25208         {
25209             if (v.match(/expression/)) { //XSS?? should we even bother..
25210                 node.removeAttribute(n);
25211                 return;
25212             }
25213             
25214             
25215             var parts = v.split(/;/);
25216             Roo.each(parts, function(p) {
25217                 p = p.replace(/\s+/g,'');
25218                 if (!p.length) {
25219                     return true;
25220                 }
25221                 var l = p.split(':').shift().replace(/\s+/g,'');
25222                 
25223                 // only allow 'c whitelisted system attributes'
25224                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
25225                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
25226                     node.removeAttribute(n);
25227                     return false;
25228                 }
25229                 return true;
25230             });
25231             
25232             
25233         }
25234         
25235         
25236         for (var i = node.attributes.length-1; i > -1 ; i--) {
25237             var a = node.attributes[i];
25238             //console.log(a);
25239             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
25240                 node.removeAttribute(a.name);
25241                 return;
25242             }
25243             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
25244                 cleanAttr(a.name,a.value); // fixme..
25245                 return;
25246             }
25247             if (a.name == 'style') {
25248                 cleanStyle(a.name,a.value);
25249             }
25250             /// clean up MS crap..
25251             // tecnically this should be a list of valid class'es..
25252             
25253             
25254             if (a.name == 'class') {
25255                 if (a.value.match(/^Mso/)) {
25256                     node.className = '';
25257                 }
25258                 
25259                 if (a.value.match(/body/)) {
25260                     node.className = '';
25261                 }
25262             }
25263             
25264             // style cleanup!?
25265             // class cleanup?
25266             
25267         }
25268         
25269         
25270         this.cleanUpChildren(node);
25271         
25272         
25273     }
25274     
25275     
25276     // hide stuff that is not compatible
25277     /**
25278      * @event blur
25279      * @hide
25280      */
25281     /**
25282      * @event change
25283      * @hide
25284      */
25285     /**
25286      * @event focus
25287      * @hide
25288      */
25289     /**
25290      * @event specialkey
25291      * @hide
25292      */
25293     /**
25294      * @cfg {String} fieldClass @hide
25295      */
25296     /**
25297      * @cfg {String} focusClass @hide
25298      */
25299     /**
25300      * @cfg {String} autoCreate @hide
25301      */
25302     /**
25303      * @cfg {String} inputType @hide
25304      */
25305     /**
25306      * @cfg {String} invalidClass @hide
25307      */
25308     /**
25309      * @cfg {String} invalidText @hide
25310      */
25311     /**
25312      * @cfg {String} msgFx @hide
25313      */
25314     /**
25315      * @cfg {String} validateOnBlur @hide
25316      */
25317 });
25318
25319 Roo.form.HtmlEditor.white = [
25320         'area', 'br', 'img', 'input', 'hr', 'wbr',
25321         
25322        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25323        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25324        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25325        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25326        'table',   'ul',         'xmp', 
25327        
25328        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25329       'thead',   'tr', 
25330      
25331       'dir', 'menu', 'ol', 'ul', 'dl',
25332        
25333       'embed',  'object'
25334 ];
25335
25336
25337 Roo.form.HtmlEditor.black = [
25338     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25339         'applet', // 
25340         'base',   'basefont', 'bgsound', 'blink',  'body', 
25341         'frame',  'frameset', 'head',    'html',   'ilayer', 
25342         'iframe', 'layer',  'link',     'meta',    'object',   
25343         'script', 'style' ,'title',  'xml' // clean later..
25344 ];
25345 Roo.form.HtmlEditor.clean = [
25346     'script', 'style', 'title', 'xml'
25347 ];
25348 Roo.form.HtmlEditor.remove = [
25349     'font'
25350 ];
25351 // attributes..
25352
25353 Roo.form.HtmlEditor.ablack = [
25354     'on'
25355 ];
25356     
25357 Roo.form.HtmlEditor.aclean = [ 
25358     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
25359 ];
25360
25361 // protocols..
25362 Roo.form.HtmlEditor.pwhite= [
25363         'http',  'https',  'mailto'
25364 ];
25365
25366 // white listed style attributes.
25367 Roo.form.HtmlEditor.cwhite= [
25368         'text-align',
25369         'font-size'
25370 ];
25371
25372
25373 Roo.form.HtmlEditor.swapCodes   =[ 
25374     [    8211, "--" ], 
25375     [    8212, "--" ], 
25376     [    8216,  "'" ],  
25377     [    8217, "'" ],  
25378     [    8220, '"' ],  
25379     [    8221, '"' ],  
25380     [    8226, "*" ],  
25381     [    8230, "..." ]
25382 ]; 
25383
25384     // <script type="text/javascript">
25385 /*
25386  * Based on
25387  * Ext JS Library 1.1.1
25388  * Copyright(c) 2006-2007, Ext JS, LLC.
25389  *  
25390  
25391  */
25392
25393 /**
25394  * @class Roo.form.HtmlEditorToolbar1
25395  * Basic Toolbar
25396  * 
25397  * Usage:
25398  *
25399  new Roo.form.HtmlEditor({
25400     ....
25401     toolbars : [
25402         new Roo.form.HtmlEditorToolbar1({
25403             disable : { fonts: 1 , format: 1, ..., ... , ...],
25404             btns : [ .... ]
25405         })
25406     }
25407      
25408  * 
25409  * @cfg {Object} disable List of elements to disable..
25410  * @cfg {Array} btns List of additional buttons.
25411  * 
25412  * 
25413  * NEEDS Extra CSS? 
25414  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
25415  */
25416  
25417 Roo.form.HtmlEditor.ToolbarStandard = function(config)
25418 {
25419     
25420     Roo.apply(this, config);
25421     
25422     // default disabled, based on 'good practice'..
25423     this.disable = this.disable || {};
25424     Roo.applyIf(this.disable, {
25425         fontSize : true,
25426         colors : true,
25427         specialElements : true
25428     });
25429     
25430     
25431     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
25432     // dont call parent... till later.
25433 }
25434
25435 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
25436     
25437     tb: false,
25438     
25439     rendered: false,
25440     
25441     editor : false,
25442     /**
25443      * @cfg {Object} disable  List of toolbar elements to disable
25444          
25445      */
25446     disable : false,
25447       /**
25448      * @cfg {Array} fontFamilies An array of available font families
25449      */
25450     fontFamilies : [
25451         'Arial',
25452         'Courier New',
25453         'Tahoma',
25454         'Times New Roman',
25455         'Verdana'
25456     ],
25457     
25458     specialChars : [
25459            "&#169;",
25460           "&#174;",     
25461           "&#8482;",    
25462           "&#163;" ,    
25463          // "&#8212;",    
25464           "&#8230;",    
25465           "&#247;" ,    
25466         //  "&#225;" ,     ?? a acute?
25467            "&#8364;"    , //Euro
25468        //   "&#8220;"    ,
25469         //  "&#8221;"    ,
25470         //  "&#8226;"    ,
25471           "&#176;"  //   , // degrees
25472
25473          // "&#233;"     , // e ecute
25474          // "&#250;"     , // u ecute?
25475     ],
25476     
25477     specialElements : [
25478         {
25479             text: "Insert Table",
25480             xtype: 'MenuItem',
25481             xns : Roo.Menu,
25482             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
25483                 
25484         },
25485         {    
25486             text: "Insert Image",
25487             xtype: 'MenuItem',
25488             xns : Roo.Menu,
25489             ihtml : '<img src="about:blank"/>'
25490             
25491         }
25492         
25493          
25494     ],
25495     
25496     
25497     inputElements : [ 
25498             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
25499             "input:submit", "input:button", "select", "textarea", "label" ],
25500     formats : [
25501         ["p"] ,  
25502         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
25503         ["pre"],[ "code"], 
25504         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
25505     ],
25506      /**
25507      * @cfg {String} defaultFont default font to use.
25508      */
25509     defaultFont: 'tahoma',
25510    
25511     fontSelect : false,
25512     
25513     
25514     formatCombo : false,
25515     
25516     init : function(editor)
25517     {
25518         this.editor = editor;
25519         
25520         
25521         var fid = editor.frameId;
25522         var etb = this;
25523         function btn(id, toggle, handler){
25524             var xid = fid + '-'+ id ;
25525             return {
25526                 id : xid,
25527                 cmd : id,
25528                 cls : 'x-btn-icon x-edit-'+id,
25529                 enableToggle:toggle !== false,
25530                 scope: editor, // was editor...
25531                 handler:handler||editor.relayBtnCmd,
25532                 clickEvent:'mousedown',
25533                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
25534                 tabIndex:-1
25535             };
25536         }
25537         
25538         
25539         
25540         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
25541         this.tb = tb;
25542          // stop form submits
25543         tb.el.on('click', function(e){
25544             e.preventDefault(); // what does this do?
25545         });
25546
25547         if(!this.disable.font && !Roo.isSafari){
25548             /* why no safari for fonts
25549             editor.fontSelect = tb.el.createChild({
25550                 tag:'select',
25551                 tabIndex: -1,
25552                 cls:'x-font-select',
25553                 html: editor.createFontOptions()
25554             });
25555             editor.fontSelect.on('change', function(){
25556                 var font = editor.fontSelect.dom.value;
25557                 editor.relayCmd('fontname', font);
25558                 editor.deferFocus();
25559             }, editor);
25560             tb.add(
25561                 editor.fontSelect.dom,
25562                 '-'
25563             );
25564             */
25565         };
25566         if(!this.disable.formats){
25567             this.formatCombo = new Roo.form.ComboBox({
25568                 store: new Roo.data.SimpleStore({
25569                     id : 'tag',
25570                     fields: ['tag'],
25571                     data : this.formats // from states.js
25572                 }),
25573                 blockFocus : true,
25574                 //autoCreate : {tag: "div",  size: "20"},
25575                 displayField:'tag',
25576                 typeAhead: false,
25577                 mode: 'local',
25578                 editable : false,
25579                 triggerAction: 'all',
25580                 emptyText:'Add tag',
25581                 selectOnFocus:true,
25582                 width:135,
25583                 listeners : {
25584                     'select': function(c, r, i) {
25585                         editor.insertTag(r.get('tag'));
25586                         editor.focus();
25587                     }
25588                 }
25589
25590             });
25591             tb.addField(this.formatCombo);
25592             
25593         }
25594         
25595         if(!this.disable.format){
25596             tb.add(
25597                 btn('bold'),
25598                 btn('italic'),
25599                 btn('underline')
25600             );
25601         };
25602         if(!this.disable.fontSize){
25603             tb.add(
25604                 '-',
25605                 
25606                 
25607                 btn('increasefontsize', false, editor.adjustFont),
25608                 btn('decreasefontsize', false, editor.adjustFont)
25609             );
25610         };
25611         
25612         
25613         if(!this.disable.colors){
25614             tb.add(
25615                 '-', {
25616                     id:editor.frameId +'-forecolor',
25617                     cls:'x-btn-icon x-edit-forecolor',
25618                     clickEvent:'mousedown',
25619                     tooltip: this.buttonTips['forecolor'] || undefined,
25620                     tabIndex:-1,
25621                     menu : new Roo.menu.ColorMenu({
25622                         allowReselect: true,
25623                         focus: Roo.emptyFn,
25624                         value:'000000',
25625                         plain:true,
25626                         selectHandler: function(cp, color){
25627                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
25628                             editor.deferFocus();
25629                         },
25630                         scope: editor,
25631                         clickEvent:'mousedown'
25632                     })
25633                 }, {
25634                     id:editor.frameId +'backcolor',
25635                     cls:'x-btn-icon x-edit-backcolor',
25636                     clickEvent:'mousedown',
25637                     tooltip: this.buttonTips['backcolor'] || undefined,
25638                     tabIndex:-1,
25639                     menu : new Roo.menu.ColorMenu({
25640                         focus: Roo.emptyFn,
25641                         value:'FFFFFF',
25642                         plain:true,
25643                         allowReselect: true,
25644                         selectHandler: function(cp, color){
25645                             if(Roo.isGecko){
25646                                 editor.execCmd('useCSS', false);
25647                                 editor.execCmd('hilitecolor', color);
25648                                 editor.execCmd('useCSS', true);
25649                                 editor.deferFocus();
25650                             }else{
25651                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
25652                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
25653                                 editor.deferFocus();
25654                             }
25655                         },
25656                         scope:editor,
25657                         clickEvent:'mousedown'
25658                     })
25659                 }
25660             );
25661         };
25662         // now add all the items...
25663         
25664
25665         if(!this.disable.alignments){
25666             tb.add(
25667                 '-',
25668                 btn('justifyleft'),
25669                 btn('justifycenter'),
25670                 btn('justifyright')
25671             );
25672         };
25673
25674         //if(!Roo.isSafari){
25675             if(!this.disable.links){
25676                 tb.add(
25677                     '-',
25678                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
25679                 );
25680             };
25681
25682             if(!this.disable.lists){
25683                 tb.add(
25684                     '-',
25685                     btn('insertorderedlist'),
25686                     btn('insertunorderedlist')
25687                 );
25688             }
25689             if(!this.disable.sourceEdit){
25690                 tb.add(
25691                     '-',
25692                     btn('sourceedit', true, function(btn){
25693                         this.toggleSourceEdit(btn.pressed);
25694                     })
25695                 );
25696             }
25697         //}
25698         
25699         var smenu = { };
25700         // special menu.. - needs to be tidied up..
25701         if (!this.disable.special) {
25702             smenu = {
25703                 text: "&#169;",
25704                 cls: 'x-edit-none',
25705                 
25706                 menu : {
25707                     items : []
25708                 }
25709             };
25710             for (var i =0; i < this.specialChars.length; i++) {
25711                 smenu.menu.items.push({
25712                     
25713                     html: this.specialChars[i],
25714                     handler: function(a,b) {
25715                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
25716                         
25717                     },
25718                     tabIndex:-1
25719                 });
25720             }
25721             
25722             
25723             tb.add(smenu);
25724             
25725             
25726         }
25727          
25728         if (!this.disable.specialElements) {
25729             var semenu = {
25730                 text: "Other;",
25731                 cls: 'x-edit-none',
25732                 menu : {
25733                     items : []
25734                 }
25735             };
25736             for (var i =0; i < this.specialElements.length; i++) {
25737                 semenu.menu.items.push(
25738                     Roo.apply({ 
25739                         handler: function(a,b) {
25740                             editor.insertAtCursor(this.ihtml);
25741                         }
25742                     }, this.specialElements[i])
25743                 );
25744                     
25745             }
25746             
25747             tb.add(semenu);
25748             
25749             
25750         }
25751          
25752         
25753         if (this.btns) {
25754             for(var i =0; i< this.btns.length;i++) {
25755                 var b = this.btns[i];
25756                 b.cls =  'x-edit-none';
25757                 b.scope = editor;
25758                 tb.add(b);
25759             }
25760         
25761         }
25762         
25763         
25764         
25765         // disable everything...
25766         
25767         this.tb.items.each(function(item){
25768            if(item.id != editor.frameId+ '-sourceedit'){
25769                 item.disable();
25770             }
25771         });
25772         this.rendered = true;
25773         
25774         // the all the btns;
25775         editor.on('editorevent', this.updateToolbar, this);
25776         // other toolbars need to implement this..
25777         //editor.on('editmodechange', this.updateToolbar, this);
25778     },
25779     
25780     
25781     
25782     /**
25783      * Protected method that will not generally be called directly. It triggers
25784      * a toolbar update by reading the markup state of the current selection in the editor.
25785      */
25786     updateToolbar: function(){
25787
25788         if(!this.editor.activated){
25789             this.editor.onFirstFocus();
25790             return;
25791         }
25792
25793         var btns = this.tb.items.map, 
25794             doc = this.editor.doc,
25795             frameId = this.editor.frameId;
25796
25797         if(!this.disable.font && !Roo.isSafari){
25798             /*
25799             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
25800             if(name != this.fontSelect.dom.value){
25801                 this.fontSelect.dom.value = name;
25802             }
25803             */
25804         }
25805         if(!this.disable.format){
25806             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
25807             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
25808             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
25809         }
25810         if(!this.disable.alignments){
25811             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
25812             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
25813             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
25814         }
25815         if(!Roo.isSafari && !this.disable.lists){
25816             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
25817             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
25818         }
25819         
25820         var ans = this.editor.getAllAncestors();
25821         if (this.formatCombo) {
25822             
25823             
25824             var store = this.formatCombo.store;
25825             this.formatCombo.setValue("");
25826             for (var i =0; i < ans.length;i++) {
25827                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
25828                     // select it..
25829                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
25830                     break;
25831                 }
25832             }
25833         }
25834         
25835         
25836         
25837         // hides menus... - so this cant be on a menu...
25838         Roo.menu.MenuMgr.hideAll();
25839
25840         //this.editorsyncValue();
25841     },
25842    
25843     
25844     createFontOptions : function(){
25845         var buf = [], fs = this.fontFamilies, ff, lc;
25846         for(var i = 0, len = fs.length; i< len; i++){
25847             ff = fs[i];
25848             lc = ff.toLowerCase();
25849             buf.push(
25850                 '<option value="',lc,'" style="font-family:',ff,';"',
25851                     (this.defaultFont == lc ? ' selected="true">' : '>'),
25852                     ff,
25853                 '</option>'
25854             );
25855         }
25856         return buf.join('');
25857     },
25858     
25859     toggleSourceEdit : function(sourceEditMode){
25860         if(sourceEditMode === undefined){
25861             sourceEditMode = !this.sourceEditMode;
25862         }
25863         this.sourceEditMode = sourceEditMode === true;
25864         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
25865         // just toggle the button?
25866         if(btn.pressed !== this.editor.sourceEditMode){
25867             btn.toggle(this.editor.sourceEditMode);
25868             return;
25869         }
25870         
25871         if(this.sourceEditMode){
25872             this.tb.items.each(function(item){
25873                 if(item.cmd != 'sourceedit'){
25874                     item.disable();
25875                 }
25876             });
25877           
25878         }else{
25879             if(this.initialized){
25880                 this.tb.items.each(function(item){
25881                     item.enable();
25882                 });
25883             }
25884             
25885         }
25886         // tell the editor that it's been pressed..
25887         this.editor.toggleSourceEdit(sourceEditMode);
25888        
25889     },
25890      /**
25891      * Object collection of toolbar tooltips for the buttons in the editor. The key
25892      * is the command id associated with that button and the value is a valid QuickTips object.
25893      * For example:
25894 <pre><code>
25895 {
25896     bold : {
25897         title: 'Bold (Ctrl+B)',
25898         text: 'Make the selected text bold.',
25899         cls: 'x-html-editor-tip'
25900     },
25901     italic : {
25902         title: 'Italic (Ctrl+I)',
25903         text: 'Make the selected text italic.',
25904         cls: 'x-html-editor-tip'
25905     },
25906     ...
25907 </code></pre>
25908     * @type Object
25909      */
25910     buttonTips : {
25911         bold : {
25912             title: 'Bold (Ctrl+B)',
25913             text: 'Make the selected text bold.',
25914             cls: 'x-html-editor-tip'
25915         },
25916         italic : {
25917             title: 'Italic (Ctrl+I)',
25918             text: 'Make the selected text italic.',
25919             cls: 'x-html-editor-tip'
25920         },
25921         underline : {
25922             title: 'Underline (Ctrl+U)',
25923             text: 'Underline the selected text.',
25924             cls: 'x-html-editor-tip'
25925         },
25926         increasefontsize : {
25927             title: 'Grow Text',
25928             text: 'Increase the font size.',
25929             cls: 'x-html-editor-tip'
25930         },
25931         decreasefontsize : {
25932             title: 'Shrink Text',
25933             text: 'Decrease the font size.',
25934             cls: 'x-html-editor-tip'
25935         },
25936         backcolor : {
25937             title: 'Text Highlight Color',
25938             text: 'Change the background color of the selected text.',
25939             cls: 'x-html-editor-tip'
25940         },
25941         forecolor : {
25942             title: 'Font Color',
25943             text: 'Change the color of the selected text.',
25944             cls: 'x-html-editor-tip'
25945         },
25946         justifyleft : {
25947             title: 'Align Text Left',
25948             text: 'Align text to the left.',
25949             cls: 'x-html-editor-tip'
25950         },
25951         justifycenter : {
25952             title: 'Center Text',
25953             text: 'Center text in the editor.',
25954             cls: 'x-html-editor-tip'
25955         },
25956         justifyright : {
25957             title: 'Align Text Right',
25958             text: 'Align text to the right.',
25959             cls: 'x-html-editor-tip'
25960         },
25961         insertunorderedlist : {
25962             title: 'Bullet List',
25963             text: 'Start a bulleted list.',
25964             cls: 'x-html-editor-tip'
25965         },
25966         insertorderedlist : {
25967             title: 'Numbered List',
25968             text: 'Start a numbered list.',
25969             cls: 'x-html-editor-tip'
25970         },
25971         createlink : {
25972             title: 'Hyperlink',
25973             text: 'Make the selected text a hyperlink.',
25974             cls: 'x-html-editor-tip'
25975         },
25976         sourceedit : {
25977             title: 'Source Edit',
25978             text: 'Switch to source editing mode.',
25979             cls: 'x-html-editor-tip'
25980         }
25981     },
25982     // private
25983     onDestroy : function(){
25984         if(this.rendered){
25985             
25986             this.tb.items.each(function(item){
25987                 if(item.menu){
25988                     item.menu.removeAll();
25989                     if(item.menu.el){
25990                         item.menu.el.destroy();
25991                     }
25992                 }
25993                 item.destroy();
25994             });
25995              
25996         }
25997     },
25998     onFirstFocus: function() {
25999         this.tb.items.each(function(item){
26000            item.enable();
26001         });
26002     }
26003 });
26004
26005
26006
26007
26008 // <script type="text/javascript">
26009 /*
26010  * Based on
26011  * Ext JS Library 1.1.1
26012  * Copyright(c) 2006-2007, Ext JS, LLC.
26013  *  
26014  
26015  */
26016
26017  
26018 /**
26019  * @class Roo.form.HtmlEditor.ToolbarContext
26020  * Context Toolbar
26021  * 
26022  * Usage:
26023  *
26024  new Roo.form.HtmlEditor({
26025     ....
26026     toolbars : [
26027         { xtype: 'ToolbarStandard', styles : {} }
26028         { xtype: 'ToolbarContext', disable : {} }
26029     ]
26030 })
26031
26032      
26033  * 
26034  * @config : {Object} disable List of elements to disable.. (not done yet.)
26035  * @config : {Object} styles  Map of styles available.
26036  * 
26037  */
26038
26039 Roo.form.HtmlEditor.ToolbarContext = function(config)
26040 {
26041     
26042     Roo.apply(this, config);
26043     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26044     // dont call parent... till later.
26045     this.styles = this.styles || {};
26046 }
26047 Roo.form.HtmlEditor.ToolbarContext.types = {
26048     'IMG' : {
26049         width : {
26050             title: "Width",
26051             width: 40
26052         },
26053         height:  {
26054             title: "Height",
26055             width: 40
26056         },
26057         align: {
26058             title: "Align",
26059             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
26060             width : 80
26061             
26062         },
26063         border: {
26064             title: "Border",
26065             width: 40
26066         },
26067         alt: {
26068             title: "Alt",
26069             width: 120
26070         },
26071         src : {
26072             title: "Src",
26073             width: 220
26074         }
26075         
26076     },
26077     'A' : {
26078         name : {
26079             title: "Name",
26080             width: 50
26081         },
26082         href:  {
26083             title: "Href",
26084             width: 220
26085         } // border?
26086         
26087     },
26088     'TABLE' : {
26089         rows : {
26090             title: "Rows",
26091             width: 20
26092         },
26093         cols : {
26094             title: "Cols",
26095             width: 20
26096         },
26097         width : {
26098             title: "Width",
26099             width: 40
26100         },
26101         height : {
26102             title: "Height",
26103             width: 40
26104         },
26105         border : {
26106             title: "Border",
26107             width: 20
26108         }
26109     },
26110     'TD' : {
26111         width : {
26112             title: "Width",
26113             width: 40
26114         },
26115         height : {
26116             title: "Height",
26117             width: 40
26118         },   
26119         align: {
26120             title: "Align",
26121             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
26122             width: 80
26123         },
26124         valign: {
26125             title: "Valign",
26126             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
26127             width: 80
26128         },
26129         colspan: {
26130             title: "Colspan",
26131             width: 20
26132             
26133         }
26134     },
26135     'INPUT' : {
26136         name : {
26137             title: "name",
26138             width: 120
26139         },
26140         value : {
26141             title: "Value",
26142             width: 120
26143         },
26144         width : {
26145             title: "Width",
26146             width: 40
26147         }
26148     },
26149     'LABEL' : {
26150         'for' : {
26151             title: "For",
26152             width: 120
26153         }
26154     },
26155     'TEXTAREA' : {
26156           name : {
26157             title: "name",
26158             width: 120
26159         },
26160         rows : {
26161             title: "Rows",
26162             width: 20
26163         },
26164         cols : {
26165             title: "Cols",
26166             width: 20
26167         }
26168     },
26169     'SELECT' : {
26170         name : {
26171             title: "name",
26172             width: 120
26173         },
26174         selectoptions : {
26175             title: "Options",
26176             width: 200
26177         }
26178     },
26179     
26180     // should we really allow this??
26181     // should this just be 
26182     'BODY' : {
26183         title : {
26184             title: "title",
26185             width: 200,
26186             disabled : true
26187         }
26188     },
26189     '*' : {
26190         // empty..
26191     }
26192 };
26193
26194
26195
26196 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
26197     
26198     tb: false,
26199     
26200     rendered: false,
26201     
26202     editor : false,
26203     /**
26204      * @cfg {Object} disable  List of toolbar elements to disable
26205          
26206      */
26207     disable : false,
26208     /**
26209      * @cfg {Object} styles List of styles 
26210      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
26211      *
26212      * These must be defined in the page, so they get rendered correctly..
26213      * .headline { }
26214      * TD.underline { }
26215      * 
26216      */
26217     styles : false,
26218     
26219     
26220     
26221     toolbars : false,
26222     
26223     init : function(editor)
26224     {
26225         this.editor = editor;
26226         
26227         
26228         var fid = editor.frameId;
26229         var etb = this;
26230         function btn(id, toggle, handler){
26231             var xid = fid + '-'+ id ;
26232             return {
26233                 id : xid,
26234                 cmd : id,
26235                 cls : 'x-btn-icon x-edit-'+id,
26236                 enableToggle:toggle !== false,
26237                 scope: editor, // was editor...
26238                 handler:handler||editor.relayBtnCmd,
26239                 clickEvent:'mousedown',
26240                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
26241                 tabIndex:-1
26242             };
26243         }
26244         // create a new element.
26245         var wdiv = editor.wrap.createChild({
26246                 tag: 'div'
26247             }, editor.wrap.dom.firstChild.nextSibling, true);
26248         
26249         // can we do this more than once??
26250         
26251          // stop form submits
26252       
26253  
26254         // disable everything...
26255         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
26256         this.toolbars = {};
26257            
26258         for (var i in  ty) {
26259           
26260             this.toolbars[i] = this.buildToolbar(ty[i],i);
26261         }
26262         this.tb = this.toolbars.BODY;
26263         this.tb.el.show();
26264         this.buildFooter();
26265         this.footer.show();
26266          
26267         this.rendered = true;
26268         
26269         // the all the btns;
26270         editor.on('editorevent', this.updateToolbar, this);
26271         // other toolbars need to implement this..
26272         //editor.on('editmodechange', this.updateToolbar, this);
26273     },
26274     
26275     
26276     
26277     /**
26278      * Protected method that will not generally be called directly. It triggers
26279      * a toolbar update by reading the markup state of the current selection in the editor.
26280      */
26281     updateToolbar: function(ignore_a,ignore_b,sel){
26282
26283         
26284         if(!this.editor.activated){
26285              this.editor.onFirstFocus();
26286             return;
26287         }
26288         var updateFooter = sel ? false : true;
26289         
26290         
26291         var ans = this.editor.getAllAncestors();
26292         
26293         // pick
26294         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
26295         
26296         if (!sel) { 
26297             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
26298             sel = sel ? sel : this.editor.doc.body;
26299             sel = sel.tagName.length ? sel : this.editor.doc.body;
26300             
26301         }
26302         // pick a menu that exists..
26303         var tn = sel.tagName.toUpperCase();
26304         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
26305         
26306         tn = sel.tagName.toUpperCase();
26307         
26308         var lastSel = this.tb.selectedNode
26309         
26310         this.tb.selectedNode = sel;
26311         
26312         // if current menu does not match..
26313         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
26314                 
26315             this.tb.el.hide();
26316             ///console.log("show: " + tn);
26317             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
26318             this.tb.el.show();
26319             // update name
26320             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
26321             
26322             
26323             // update attributes
26324             if (this.tb.fields) {
26325                 this.tb.fields.each(function(e) {
26326                    e.setValue(sel.getAttribute(e.name));
26327                 });
26328             }
26329             
26330             // update styles
26331             var st = this.tb.fields.item(0);
26332             st.store.removeAll();
26333             var cn = sel.className.split(/\s+/);
26334             
26335             var avs = [];
26336             if (this.styles['*']) {
26337                 
26338                 Roo.each(this.styles['*'], function(v) {
26339                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
26340                 });
26341             }
26342             if (this.styles[tn]) { 
26343                 Roo.each(this.styles[tn], function(v) {
26344                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
26345                 });
26346             }
26347             
26348             st.store.loadData(avs);
26349             st.collapse();
26350             st.setValue(cn);
26351             
26352             // flag our selected Node.
26353             this.tb.selectedNode = sel;
26354            
26355            
26356             Roo.menu.MenuMgr.hideAll();
26357
26358         }
26359         
26360         if (!updateFooter) {
26361             return;
26362         }
26363         // update the footer
26364         //
26365         var html = '';
26366         
26367         this.footerEls = ans.reverse();
26368         Roo.each(this.footerEls, function(a,i) {
26369             if (!a) { return; }
26370             html += html.length ? ' &gt; '  :  '';
26371             
26372             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
26373             
26374         });
26375        
26376         // 
26377         var sz = this.footDisp.up('td').getSize();
26378         this.footDisp.dom.style.width = (sz.width -10) + 'px';
26379         this.footDisp.dom.style.marginLeft = '5px';
26380         
26381         this.footDisp.dom.style.overflow = 'hidden';
26382         
26383         this.footDisp.dom.innerHTML = html;
26384             
26385         //this.editorsyncValue();
26386     },
26387    
26388        
26389     // private
26390     onDestroy : function(){
26391         if(this.rendered){
26392             
26393             this.tb.items.each(function(item){
26394                 if(item.menu){
26395                     item.menu.removeAll();
26396                     if(item.menu.el){
26397                         item.menu.el.destroy();
26398                     }
26399                 }
26400                 item.destroy();
26401             });
26402              
26403         }
26404     },
26405     onFirstFocus: function() {
26406         // need to do this for all the toolbars..
26407         this.tb.items.each(function(item){
26408            item.enable();
26409         });
26410     },
26411     buildToolbar: function(tlist, nm)
26412     {
26413         var editor = this.editor;
26414          // create a new element.
26415         var wdiv = editor.wrap.createChild({
26416                 tag: 'div'
26417             }, editor.wrap.dom.firstChild.nextSibling, true);
26418         
26419        
26420         var tb = new Roo.Toolbar(wdiv);
26421         // add the name..
26422         
26423         tb.add(nm+ ":&nbsp;");
26424         
26425         // styles...
26426         if (this.styles) {
26427             
26428             // this needs a multi-select checkbox...
26429             tb.addField( new Roo.form.ComboBox({
26430                 store: new Roo.data.SimpleStore({
26431                     id : 'val',
26432                     fields: ['val', 'selected'],
26433                     data : [] 
26434                 }),
26435                 name : 'className',
26436                 displayField:'val',
26437                 typeAhead: false,
26438                 mode: 'local',
26439                 editable : false,
26440                 triggerAction: 'all',
26441                 emptyText:'Select Style',
26442                 selectOnFocus:true,
26443                 width: 130,
26444                 listeners : {
26445                     'select': function(c, r, i) {
26446                         // initial support only for on class per el..
26447                         tb.selectedNode.className =  r ? r.get('val') : '';
26448                     }
26449                 }
26450     
26451             }));
26452         }
26453             
26454         
26455         
26456         for (var i in tlist) {
26457             
26458             var item = tlist[i];
26459             tb.add(item.title + ":&nbsp;");
26460             
26461             
26462             
26463             
26464             if (item.opts) {
26465                 // opts == pulldown..
26466                 tb.addField( new Roo.form.ComboBox({
26467                     store: new Roo.data.SimpleStore({
26468                         id : 'val',
26469                         fields: ['val'],
26470                         data : item.opts  
26471                     }),
26472                     name : i,
26473                     displayField:'val',
26474                     typeAhead: false,
26475                     mode: 'local',
26476                     editable : false,
26477                     triggerAction: 'all',
26478                     emptyText:'Select',
26479                     selectOnFocus:true,
26480                     width: item.width ? item.width  : 130,
26481                     listeners : {
26482                         'select': function(c, r, i) {
26483                             tb.selectedNode.setAttribute(c.name, r.get('val'));
26484                         }
26485                     }
26486
26487                 }));
26488                 continue;
26489                     
26490                  
26491                 
26492                 tb.addField( new Roo.form.TextField({
26493                     name: i,
26494                     width: 100,
26495                     //allowBlank:false,
26496                     value: ''
26497                 }));
26498                 continue;
26499             }
26500             tb.addField( new Roo.form.TextField({
26501                 name: i,
26502                 width: item.width,
26503                 //allowBlank:true,
26504                 value: '',
26505                 listeners: {
26506                     'change' : function(f, nv, ov) {
26507                         tb.selectedNode.setAttribute(f.name, nv);
26508                     }
26509                 }
26510             }));
26511              
26512         }
26513         tb.el.on('click', function(e){
26514             e.preventDefault(); // what does this do?
26515         });
26516         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
26517         tb.el.hide();
26518         tb.name = nm;
26519         // dont need to disable them... as they will get hidden
26520         return tb;
26521          
26522         
26523     },
26524     buildFooter : function()
26525     {
26526         
26527         var fel = this.editor.wrap.createChild();
26528         this.footer = new Roo.Toolbar(fel);
26529         // toolbar has scrolly on left / right?
26530         var footDisp= new Roo.Toolbar.Fill();
26531         var _t = this;
26532         this.footer.add(
26533             {
26534                 text : '&lt;',
26535                 xtype: 'Button',
26536                 handler : function() {
26537                     _t.footDisp.scrollTo('left',0,true)
26538                 }
26539             }
26540         );
26541         this.footer.add( footDisp );
26542         this.footer.add( 
26543             {
26544                 text : '&gt;',
26545                 xtype: 'Button',
26546                 handler : function() {
26547                     // no animation..
26548                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
26549                 }
26550             }
26551         );
26552         var fel = Roo.get(footDisp.el);
26553         fel.addClass('x-editor-context');
26554         this.footDispWrap = fel; 
26555         this.footDispWrap.overflow  = 'hidden';
26556         
26557         this.footDisp = fel.createChild();
26558         this.footDispWrap.on('click', this.onContextClick, this)
26559         
26560         
26561     },
26562     onContextClick : function (ev,dom)
26563     {
26564         ev.preventDefault();
26565         var  cn = dom.className;
26566         Roo.log(cn);
26567         if (!cn.match(/x-ed-loc-/)) {
26568             return;
26569         }
26570         var n = cn.split('-').pop();
26571         var ans = this.footerEls;
26572         var sel = ans[n];
26573         
26574          // pick
26575         var range = this.editor.createRange();
26576         
26577         range.selectNodeContents(sel);
26578         //range.selectNode(sel);
26579         
26580         
26581         var selection = this.editor.getSelection();
26582         selection.removeAllRanges();
26583         selection.addRange(range);
26584         
26585         
26586         
26587         this.updateToolbar(null, null, sel);
26588         
26589         
26590     }
26591     
26592     
26593     
26594     
26595     
26596 });
26597
26598
26599
26600
26601
26602 /*
26603  * Based on:
26604  * Ext JS Library 1.1.1
26605  * Copyright(c) 2006-2007, Ext JS, LLC.
26606  *
26607  * Originally Released Under LGPL - original licence link has changed is not relivant.
26608  *
26609  * Fork - LGPL
26610  * <script type="text/javascript">
26611  */
26612  
26613 /**
26614  * @class Roo.form.BasicForm
26615  * @extends Roo.util.Observable
26616  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
26617  * @constructor
26618  * @param {String/HTMLElement/Roo.Element} el The form element or its id
26619  * @param {Object} config Configuration options
26620  */
26621 Roo.form.BasicForm = function(el, config){
26622     this.allItems = [];
26623     this.childForms = [];
26624     Roo.apply(this, config);
26625     /*
26626      * The Roo.form.Field items in this form.
26627      * @type MixedCollection
26628      */
26629      
26630      
26631     this.items = new Roo.util.MixedCollection(false, function(o){
26632         return o.id || (o.id = Roo.id());
26633     });
26634     this.addEvents({
26635         /**
26636          * @event beforeaction
26637          * Fires before any action is performed. Return false to cancel the action.
26638          * @param {Form} this
26639          * @param {Action} action The action to be performed
26640          */
26641         beforeaction: true,
26642         /**
26643          * @event actionfailed
26644          * Fires when an action fails.
26645          * @param {Form} this
26646          * @param {Action} action The action that failed
26647          */
26648         actionfailed : true,
26649         /**
26650          * @event actioncomplete
26651          * Fires when an action is completed.
26652          * @param {Form} this
26653          * @param {Action} action The action that completed
26654          */
26655         actioncomplete : true
26656     });
26657     if(el){
26658         this.initEl(el);
26659     }
26660     Roo.form.BasicForm.superclass.constructor.call(this);
26661 };
26662
26663 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
26664     /**
26665      * @cfg {String} method
26666      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
26667      */
26668     /**
26669      * @cfg {DataReader} reader
26670      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
26671      * This is optional as there is built-in support for processing JSON.
26672      */
26673     /**
26674      * @cfg {DataReader} errorReader
26675      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
26676      * This is completely optional as there is built-in support for processing JSON.
26677      */
26678     /**
26679      * @cfg {String} url
26680      * The URL to use for form actions if one isn't supplied in the action options.
26681      */
26682     /**
26683      * @cfg {Boolean} fileUpload
26684      * Set to true if this form is a file upload.
26685      */
26686      
26687     /**
26688      * @cfg {Object} baseParams
26689      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
26690      */
26691      /**
26692      
26693     /**
26694      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
26695      */
26696     timeout: 30,
26697
26698     // private
26699     activeAction : null,
26700
26701     /**
26702      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
26703      * or setValues() data instead of when the form was first created.
26704      */
26705     trackResetOnLoad : false,
26706     
26707     
26708     /**
26709      * childForms - used for multi-tab forms
26710      * @type {Array}
26711      */
26712     childForms : false,
26713     
26714     /**
26715      * allItems - full list of fields.
26716      * @type {Array}
26717      */
26718     allItems : false,
26719     
26720     /**
26721      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
26722      * element by passing it or its id or mask the form itself by passing in true.
26723      * @type Mixed
26724      */
26725     waitMsgTarget : false,
26726
26727     // private
26728     initEl : function(el){
26729         this.el = Roo.get(el);
26730         this.id = this.el.id || Roo.id();
26731         this.el.on('submit', this.onSubmit, this);
26732         this.el.addClass('x-form');
26733     },
26734
26735     // private
26736     onSubmit : function(e){
26737         e.stopEvent();
26738     },
26739
26740     /**
26741      * Returns true if client-side validation on the form is successful.
26742      * @return Boolean
26743      */
26744     isValid : function(){
26745         var valid = true;
26746         this.items.each(function(f){
26747            if(!f.validate()){
26748                valid = false;
26749            }
26750         });
26751         return valid;
26752     },
26753
26754     /**
26755      * Returns true if any fields in this form have changed since their original load.
26756      * @return Boolean
26757      */
26758     isDirty : function(){
26759         var dirty = false;
26760         this.items.each(function(f){
26761            if(f.isDirty()){
26762                dirty = true;
26763                return false;
26764            }
26765         });
26766         return dirty;
26767     },
26768
26769     /**
26770      * Performs a predefined action (submit or load) or custom actions you define on this form.
26771      * @param {String} actionName The name of the action type
26772      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
26773      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
26774      * accept other config options):
26775      * <pre>
26776 Property          Type             Description
26777 ----------------  ---------------  ----------------------------------------------------------------------------------
26778 url               String           The url for the action (defaults to the form's url)
26779 method            String           The form method to use (defaults to the form's method, or POST if not defined)
26780 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
26781 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
26782                                    validate the form on the client (defaults to false)
26783      * </pre>
26784      * @return {BasicForm} this
26785      */
26786     doAction : function(action, options){
26787         if(typeof action == 'string'){
26788             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
26789         }
26790         if(this.fireEvent('beforeaction', this, action) !== false){
26791             this.beforeAction(action);
26792             action.run.defer(100, action);
26793         }
26794         return this;
26795     },
26796
26797     /**
26798      * Shortcut to do a submit action.
26799      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
26800      * @return {BasicForm} this
26801      */
26802     submit : function(options){
26803         this.doAction('submit', options);
26804         return this;
26805     },
26806
26807     /**
26808      * Shortcut to do a load action.
26809      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
26810      * @return {BasicForm} this
26811      */
26812     load : function(options){
26813         this.doAction('load', options);
26814         return this;
26815     },
26816
26817     /**
26818      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
26819      * @param {Record} record The record to edit
26820      * @return {BasicForm} this
26821      */
26822     updateRecord : function(record){
26823         record.beginEdit();
26824         var fs = record.fields;
26825         fs.each(function(f){
26826             var field = this.findField(f.name);
26827             if(field){
26828                 record.set(f.name, field.getValue());
26829             }
26830         }, this);
26831         record.endEdit();
26832         return this;
26833     },
26834
26835     /**
26836      * Loads an Roo.data.Record into this form.
26837      * @param {Record} record The record to load
26838      * @return {BasicForm} this
26839      */
26840     loadRecord : function(record){
26841         this.setValues(record.data);
26842         return this;
26843     },
26844
26845     // private
26846     beforeAction : function(action){
26847         var o = action.options;
26848         
26849        
26850         if(this.waitMsgTarget === true){
26851             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
26852         }else if(this.waitMsgTarget){
26853             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
26854             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
26855         }else {
26856             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
26857         }
26858          
26859     },
26860
26861     // private
26862     afterAction : function(action, success){
26863         this.activeAction = null;
26864         var o = action.options;
26865         
26866         if(this.waitMsgTarget === true){
26867             this.el.unmask();
26868         }else if(this.waitMsgTarget){
26869             this.waitMsgTarget.unmask();
26870         }else{
26871             Roo.MessageBox.updateProgress(1);
26872             Roo.MessageBox.hide();
26873         }
26874          
26875         if(success){
26876             if(o.reset){
26877                 this.reset();
26878             }
26879             Roo.callback(o.success, o.scope, [this, action]);
26880             this.fireEvent('actioncomplete', this, action);
26881             
26882         }else{
26883             Roo.callback(o.failure, o.scope, [this, action]);
26884             // show an error message if no failed handler is set..
26885             if (!this.hasListener('actionfailed')) {
26886                 Roo.MessageBox.alert("Error",
26887                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
26888                         action.result.errorMsg :
26889                         "Saving Failed, please check your entries"
26890                 );
26891             }
26892             
26893             this.fireEvent('actionfailed', this, action);
26894         }
26895         
26896     },
26897
26898     /**
26899      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
26900      * @param {String} id The value to search for
26901      * @return Field
26902      */
26903     findField : function(id){
26904         var field = this.items.get(id);
26905         if(!field){
26906             this.items.each(function(f){
26907                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
26908                     field = f;
26909                     return false;
26910                 }
26911             });
26912         }
26913         return field || null;
26914     },
26915
26916     /**
26917      * Add a secondary form to this one, 
26918      * Used to provide tabbed forms. One form is primary, with hidden values 
26919      * which mirror the elements from the other forms.
26920      * 
26921      * @param {Roo.form.Form} form to add.
26922      * 
26923      */
26924     addForm : function(form)
26925     {
26926        
26927         if (this.childForms.indexOf(form) > -1) {
26928             // already added..
26929             return;
26930         }
26931         this.childForms.push(form);
26932         var n = '';
26933         Roo.each(form.allItems, function (fe) {
26934             
26935             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
26936             if (this.findField(n)) { // already added..
26937                 return;
26938             }
26939             var add = new Roo.form.Hidden({
26940                 name : n
26941             });
26942             add.render(this.el);
26943             
26944             this.add( add );
26945         }, this);
26946         
26947     },
26948     /**
26949      * Mark fields in this form invalid in bulk.
26950      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
26951      * @return {BasicForm} this
26952      */
26953     markInvalid : function(errors){
26954         if(errors instanceof Array){
26955             for(var i = 0, len = errors.length; i < len; i++){
26956                 var fieldError = errors[i];
26957                 var f = this.findField(fieldError.id);
26958                 if(f){
26959                     f.markInvalid(fieldError.msg);
26960                 }
26961             }
26962         }else{
26963             var field, id;
26964             for(id in errors){
26965                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
26966                     field.markInvalid(errors[id]);
26967                 }
26968             }
26969         }
26970         Roo.each(this.childForms || [], function (f) {
26971             f.markInvalid(errors);
26972         });
26973         
26974         return this;
26975     },
26976
26977     /**
26978      * Set values for fields in this form in bulk.
26979      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
26980      * @return {BasicForm} this
26981      */
26982     setValues : function(values){
26983         if(values instanceof Array){ // array of objects
26984             for(var i = 0, len = values.length; i < len; i++){
26985                 var v = values[i];
26986                 var f = this.findField(v.id);
26987                 if(f){
26988                     f.setValue(v.value);
26989                     if(this.trackResetOnLoad){
26990                         f.originalValue = f.getValue();
26991                     }
26992                 }
26993             }
26994         }else{ // object hash
26995             var field, id;
26996             for(id in values){
26997                 if(typeof values[id] != 'function' && (field = this.findField(id))){
26998                     
26999                     if (field.setFromData && 
27000                         field.valueField && 
27001                         field.displayField &&
27002                         // combos' with local stores can 
27003                         // be queried via setValue()
27004                         // to set their value..
27005                         (field.store && !field.store.isLocal)
27006                         ) {
27007                         // it's a combo
27008                         var sd = { };
27009                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
27010                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
27011                         field.setFromData(sd);
27012                         
27013                     } else {
27014                         field.setValue(values[id]);
27015                     }
27016                     
27017                     
27018                     if(this.trackResetOnLoad){
27019                         field.originalValue = field.getValue();
27020                     }
27021                 }
27022             }
27023         }
27024          
27025         Roo.each(this.childForms || [], function (f) {
27026             f.setValues(values);
27027         });
27028                 
27029         return this;
27030     },
27031
27032     /**
27033      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
27034      * they are returned as an array.
27035      * @param {Boolean} asString
27036      * @return {Object}
27037      */
27038     getValues : function(asString){
27039         if (this.childForms) {
27040             // copy values from the child forms
27041             Roo.each(this.childForms, function (f) {
27042                 this.setValues(f.getValues());
27043             }, this);
27044         }
27045         
27046         
27047         
27048         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
27049         if(asString === true){
27050             return fs;
27051         }
27052         return Roo.urlDecode(fs);
27053     },
27054     
27055     /**
27056      * Returns the fields in this form as an object with key/value pairs. 
27057      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
27058      * @return {Object}
27059      */
27060     getFieldValues : function(with_hidden)
27061     {
27062         if (this.childForms) {
27063             // copy values from the child forms
27064             // should this call getFieldValues - probably not as we do not currently copy
27065             // hidden fields when we generate..
27066             Roo.each(this.childForms, function (f) {
27067                 this.setValues(f.getValues());
27068             }, this);
27069         }
27070         
27071         var ret = {};
27072         this.items.each(function(f){
27073             if (!f.getName()) {
27074                 return;
27075             }
27076             var v = f.getValue();
27077             // not sure if this supported any more..
27078             if ((typeof(v) == 'object') && f.getRawValue) {
27079                 v = f.getRawValue() ; // dates..
27080             }
27081             // combo boxes where name != hiddenName...
27082             if (f.name != f.getName()) {
27083                 ret[f.name] = f.getRawValue();
27084             }
27085             ret[f.getName()] = v;
27086         });
27087         
27088         return ret;
27089     },
27090
27091     /**
27092      * Clears all invalid messages in this form.
27093      * @return {BasicForm} this
27094      */
27095     clearInvalid : function(){
27096         this.items.each(function(f){
27097            f.clearInvalid();
27098         });
27099         
27100         Roo.each(this.childForms || [], function (f) {
27101             f.clearInvalid();
27102         });
27103         
27104         
27105         return this;
27106     },
27107
27108     /**
27109      * Resets this form.
27110      * @return {BasicForm} this
27111      */
27112     reset : function(){
27113         this.items.each(function(f){
27114             f.reset();
27115         });
27116         
27117         Roo.each(this.childForms || [], function (f) {
27118             f.reset();
27119         });
27120        
27121         
27122         return this;
27123     },
27124
27125     /**
27126      * Add Roo.form components to this form.
27127      * @param {Field} field1
27128      * @param {Field} field2 (optional)
27129      * @param {Field} etc (optional)
27130      * @return {BasicForm} this
27131      */
27132     add : function(){
27133         this.items.addAll(Array.prototype.slice.call(arguments, 0));
27134         return this;
27135     },
27136
27137
27138     /**
27139      * Removes a field from the items collection (does NOT remove its markup).
27140      * @param {Field} field
27141      * @return {BasicForm} this
27142      */
27143     remove : function(field){
27144         this.items.remove(field);
27145         return this;
27146     },
27147
27148     /**
27149      * Looks at the fields in this form, checks them for an id attribute,
27150      * and calls applyTo on the existing dom element with that id.
27151      * @return {BasicForm} this
27152      */
27153     render : function(){
27154         this.items.each(function(f){
27155             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
27156                 f.applyTo(f.id);
27157             }
27158         });
27159         return this;
27160     },
27161
27162     /**
27163      * Calls {@link Ext#apply} for all fields in this form with the passed object.
27164      * @param {Object} values
27165      * @return {BasicForm} this
27166      */
27167     applyToFields : function(o){
27168         this.items.each(function(f){
27169            Roo.apply(f, o);
27170         });
27171         return this;
27172     },
27173
27174     /**
27175      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
27176      * @param {Object} values
27177      * @return {BasicForm} this
27178      */
27179     applyIfToFields : function(o){
27180         this.items.each(function(f){
27181            Roo.applyIf(f, o);
27182         });
27183         return this;
27184     }
27185 });
27186
27187 // back compat
27188 Roo.BasicForm = Roo.form.BasicForm;/*
27189  * Based on:
27190  * Ext JS Library 1.1.1
27191  * Copyright(c) 2006-2007, Ext JS, LLC.
27192  *
27193  * Originally Released Under LGPL - original licence link has changed is not relivant.
27194  *
27195  * Fork - LGPL
27196  * <script type="text/javascript">
27197  */
27198
27199 /**
27200  * @class Roo.form.Form
27201  * @extends Roo.form.BasicForm
27202  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
27203  * @constructor
27204  * @param {Object} config Configuration options
27205  */
27206 Roo.form.Form = function(config){
27207     var xitems =  [];
27208     if (config.items) {
27209         xitems = config.items;
27210         delete config.items;
27211     }
27212    
27213     
27214     Roo.form.Form.superclass.constructor.call(this, null, config);
27215     this.url = this.url || this.action;
27216     if(!this.root){
27217         this.root = new Roo.form.Layout(Roo.applyIf({
27218             id: Roo.id()
27219         }, config));
27220     }
27221     this.active = this.root;
27222     /**
27223      * Array of all the buttons that have been added to this form via {@link addButton}
27224      * @type Array
27225      */
27226     this.buttons = [];
27227     this.allItems = [];
27228     this.addEvents({
27229         /**
27230          * @event clientvalidation
27231          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
27232          * @param {Form} this
27233          * @param {Boolean} valid true if the form has passed client-side validation
27234          */
27235         clientvalidation: true,
27236         /**
27237          * @event rendered
27238          * Fires when the form is rendered
27239          * @param {Roo.form.Form} form
27240          */
27241         rendered : true
27242     });
27243     
27244     if (this.progressUrl) {
27245             // push a hidden field onto the list of fields..
27246             this.addxtype( {
27247                     xns: Roo.form, 
27248                     xtype : 'Hidden', 
27249                     name : 'UPLOAD_IDENTIFIER' 
27250             });
27251         }
27252         
27253     
27254     Roo.each(xitems, this.addxtype, this);
27255     
27256     
27257     
27258 };
27259
27260 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
27261     /**
27262      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
27263      */
27264     /**
27265      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
27266      */
27267     /**
27268      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
27269      */
27270     buttonAlign:'center',
27271
27272     /**
27273      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
27274      */
27275     minButtonWidth:75,
27276
27277     /**
27278      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
27279      * This property cascades to child containers if not set.
27280      */
27281     labelAlign:'left',
27282
27283     /**
27284      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
27285      * fires a looping event with that state. This is required to bind buttons to the valid
27286      * state using the config value formBind:true on the button.
27287      */
27288     monitorValid : false,
27289
27290     /**
27291      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
27292      */
27293     monitorPoll : 200,
27294     
27295     /**
27296      * @cfg {String} progressUrl - Url to return progress data 
27297      */
27298     
27299     progressUrl : false,
27300   
27301     /**
27302      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
27303      * fields are added and the column is closed. If no fields are passed the column remains open
27304      * until end() is called.
27305      * @param {Object} config The config to pass to the column
27306      * @param {Field} field1 (optional)
27307      * @param {Field} field2 (optional)
27308      * @param {Field} etc (optional)
27309      * @return Column The column container object
27310      */
27311     column : function(c){
27312         var col = new Roo.form.Column(c);
27313         this.start(col);
27314         if(arguments.length > 1){ // duplicate code required because of Opera
27315             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
27316             this.end();
27317         }
27318         return col;
27319     },
27320
27321     /**
27322      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
27323      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
27324      * until end() is called.
27325      * @param {Object} config The config to pass to the fieldset
27326      * @param {Field} field1 (optional)
27327      * @param {Field} field2 (optional)
27328      * @param {Field} etc (optional)
27329      * @return FieldSet The fieldset container object
27330      */
27331     fieldset : function(c){
27332         var fs = new Roo.form.FieldSet(c);
27333         this.start(fs);
27334         if(arguments.length > 1){ // duplicate code required because of Opera
27335             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
27336             this.end();
27337         }
27338         return fs;
27339     },
27340
27341     /**
27342      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
27343      * fields are added and the container is closed. If no fields are passed the container remains open
27344      * until end() is called.
27345      * @param {Object} config The config to pass to the Layout
27346      * @param {Field} field1 (optional)
27347      * @param {Field} field2 (optional)
27348      * @param {Field} etc (optional)
27349      * @return Layout The container object
27350      */
27351     container : function(c){
27352         var l = new Roo.form.Layout(c);
27353         this.start(l);
27354         if(arguments.length > 1){ // duplicate code required because of Opera
27355             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
27356             this.end();
27357         }
27358         return l;
27359     },
27360
27361     /**
27362      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
27363      * @param {Object} container A Roo.form.Layout or subclass of Layout
27364      * @return {Form} this
27365      */
27366     start : function(c){
27367         // cascade label info
27368         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
27369         this.active.stack.push(c);
27370         c.ownerCt = this.active;
27371         this.active = c;
27372         return this;
27373     },
27374
27375     /**
27376      * Closes the current open container
27377      * @return {Form} this
27378      */
27379     end : function(){
27380         if(this.active == this.root){
27381             return this;
27382         }
27383         this.active = this.active.ownerCt;
27384         return this;
27385     },
27386
27387     /**
27388      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
27389      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
27390      * as the label of the field.
27391      * @param {Field} field1
27392      * @param {Field} field2 (optional)
27393      * @param {Field} etc. (optional)
27394      * @return {Form} this
27395      */
27396     add : function(){
27397         this.active.stack.push.apply(this.active.stack, arguments);
27398         this.allItems.push.apply(this.allItems,arguments);
27399         var r = [];
27400         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
27401             if(a[i].isFormField){
27402                 r.push(a[i]);
27403             }
27404         }
27405         if(r.length > 0){
27406             Roo.form.Form.superclass.add.apply(this, r);
27407         }
27408         return this;
27409     },
27410     
27411
27412     
27413     
27414     
27415      /**
27416      * Find any element that has been added to a form, using it's ID or name
27417      * This can include framesets, columns etc. along with regular fields..
27418      * @param {String} id - id or name to find.
27419      
27420      * @return {Element} e - or false if nothing found.
27421      */
27422     findbyId : function(id)
27423     {
27424         var ret = false;
27425         if (!id) {
27426             return ret;
27427         }
27428         Roo.each(this.allItems, function(f){
27429             if (f.id == id || f.name == id ){
27430                 ret = f;
27431                 return false;
27432             }
27433         });
27434         return ret;
27435     },
27436
27437     
27438     
27439     /**
27440      * Render this form into the passed container. This should only be called once!
27441      * @param {String/HTMLElement/Element} container The element this component should be rendered into
27442      * @return {Form} this
27443      */
27444     render : function(ct)
27445     {
27446         
27447         
27448         
27449         ct = Roo.get(ct);
27450         var o = this.autoCreate || {
27451             tag: 'form',
27452             method : this.method || 'POST',
27453             id : this.id || Roo.id()
27454         };
27455         this.initEl(ct.createChild(o));
27456
27457         this.root.render(this.el);
27458         
27459        
27460              
27461         this.items.each(function(f){
27462             f.render('x-form-el-'+f.id);
27463         });
27464
27465         if(this.buttons.length > 0){
27466             // tables are required to maintain order and for correct IE layout
27467             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
27468                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
27469                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
27470             }}, null, true);
27471             var tr = tb.getElementsByTagName('tr')[0];
27472             for(var i = 0, len = this.buttons.length; i < len; i++) {
27473                 var b = this.buttons[i];
27474                 var td = document.createElement('td');
27475                 td.className = 'x-form-btn-td';
27476                 b.render(tr.appendChild(td));
27477             }
27478         }
27479         if(this.monitorValid){ // initialize after render
27480             this.startMonitoring();
27481         }
27482         this.fireEvent('rendered', this);
27483         return this;
27484     },
27485
27486     /**
27487      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
27488      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
27489      * object or a valid Roo.DomHelper element config
27490      * @param {Function} handler The function called when the button is clicked
27491      * @param {Object} scope (optional) The scope of the handler function
27492      * @return {Roo.Button}
27493      */
27494     addButton : function(config, handler, scope){
27495         var bc = {
27496             handler: handler,
27497             scope: scope,
27498             minWidth: this.minButtonWidth,
27499             hideParent:true
27500         };
27501         if(typeof config == "string"){
27502             bc.text = config;
27503         }else{
27504             Roo.apply(bc, config);
27505         }
27506         var btn = new Roo.Button(null, bc);
27507         this.buttons.push(btn);
27508         return btn;
27509     },
27510
27511      /**
27512      * Adds a series of form elements (using the xtype property as the factory method.
27513      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
27514      * @param {Object} config 
27515      */
27516     
27517     addxtype : function()
27518     {
27519         var ar = Array.prototype.slice.call(arguments, 0);
27520         var ret = false;
27521         for(var i = 0; i < ar.length; i++) {
27522             if (!ar[i]) {
27523                 continue; // skip -- if this happends something invalid got sent, we 
27524                 // should ignore it, as basically that interface element will not show up
27525                 // and that should be pretty obvious!!
27526             }
27527             
27528             if (Roo.form[ar[i].xtype]) {
27529                 ar[i].form = this;
27530                 var fe = Roo.factory(ar[i], Roo.form);
27531                 if (!ret) {
27532                     ret = fe;
27533                 }
27534                 fe.form = this;
27535                 if (fe.store) {
27536                     fe.store.form = this;
27537                 }
27538                 if (fe.isLayout) {  
27539                          
27540                     this.start(fe);
27541                     this.allItems.push(fe);
27542                     if (fe.items && fe.addxtype) {
27543                         fe.addxtype.apply(fe, fe.items);
27544                         delete fe.items;
27545                     }
27546                      this.end();
27547                     continue;
27548                 }
27549                 
27550                 
27551                  
27552                 this.add(fe);
27553               //  console.log('adding ' + ar[i].xtype);
27554             }
27555             if (ar[i].xtype == 'Button') {  
27556                 //console.log('adding button');
27557                 //console.log(ar[i]);
27558                 this.addButton(ar[i]);
27559                 this.allItems.push(fe);
27560                 continue;
27561             }
27562             
27563             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
27564                 alert('end is not supported on xtype any more, use items');
27565             //    this.end();
27566             //    //console.log('adding end');
27567             }
27568             
27569         }
27570         return ret;
27571     },
27572     
27573     /**
27574      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
27575      * option "monitorValid"
27576      */
27577     startMonitoring : function(){
27578         if(!this.bound){
27579             this.bound = true;
27580             Roo.TaskMgr.start({
27581                 run : this.bindHandler,
27582                 interval : this.monitorPoll || 200,
27583                 scope: this
27584             });
27585         }
27586     },
27587
27588     /**
27589      * Stops monitoring of the valid state of this form
27590      */
27591     stopMonitoring : function(){
27592         this.bound = false;
27593     },
27594
27595     // private
27596     bindHandler : function(){
27597         if(!this.bound){
27598             return false; // stops binding
27599         }
27600         var valid = true;
27601         this.items.each(function(f){
27602             if(!f.isValid(true)){
27603                 valid = false;
27604                 return false;
27605             }
27606         });
27607         for(var i = 0, len = this.buttons.length; i < len; i++){
27608             var btn = this.buttons[i];
27609             if(btn.formBind === true && btn.disabled === valid){
27610                 btn.setDisabled(!valid);
27611             }
27612         }
27613         this.fireEvent('clientvalidation', this, valid);
27614     }
27615     
27616     
27617     
27618     
27619     
27620     
27621     
27622     
27623 });
27624
27625
27626 // back compat
27627 Roo.Form = Roo.form.Form;
27628 /*
27629  * Based on:
27630  * Ext JS Library 1.1.1
27631  * Copyright(c) 2006-2007, Ext JS, LLC.
27632  *
27633  * Originally Released Under LGPL - original licence link has changed is not relivant.
27634  *
27635  * Fork - LGPL
27636  * <script type="text/javascript">
27637  */
27638  
27639  /**
27640  * @class Roo.form.Action
27641  * Internal Class used to handle form actions
27642  * @constructor
27643  * @param {Roo.form.BasicForm} el The form element or its id
27644  * @param {Object} config Configuration options
27645  */
27646  
27647  
27648 // define the action interface
27649 Roo.form.Action = function(form, options){
27650     this.form = form;
27651     this.options = options || {};
27652 };
27653 /**
27654  * Client Validation Failed
27655  * @const 
27656  */
27657 Roo.form.Action.CLIENT_INVALID = 'client';
27658 /**
27659  * Server Validation Failed
27660  * @const 
27661  */
27662  Roo.form.Action.SERVER_INVALID = 'server';
27663  /**
27664  * Connect to Server Failed
27665  * @const 
27666  */
27667 Roo.form.Action.CONNECT_FAILURE = 'connect';
27668 /**
27669  * Reading Data from Server Failed
27670  * @const 
27671  */
27672 Roo.form.Action.LOAD_FAILURE = 'load';
27673
27674 Roo.form.Action.prototype = {
27675     type : 'default',
27676     failureType : undefined,
27677     response : undefined,
27678     result : undefined,
27679
27680     // interface method
27681     run : function(options){
27682
27683     },
27684
27685     // interface method
27686     success : function(response){
27687
27688     },
27689
27690     // interface method
27691     handleResponse : function(response){
27692
27693     },
27694
27695     // default connection failure
27696     failure : function(response){
27697         
27698         this.response = response;
27699         this.failureType = Roo.form.Action.CONNECT_FAILURE;
27700         this.form.afterAction(this, false);
27701     },
27702
27703     processResponse : function(response){
27704         this.response = response;
27705         if(!response.responseText){
27706             return true;
27707         }
27708         this.result = this.handleResponse(response);
27709         return this.result;
27710     },
27711
27712     // utility functions used internally
27713     getUrl : function(appendParams){
27714         var url = this.options.url || this.form.url || this.form.el.dom.action;
27715         if(appendParams){
27716             var p = this.getParams();
27717             if(p){
27718                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
27719             }
27720         }
27721         return url;
27722     },
27723
27724     getMethod : function(){
27725         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
27726     },
27727
27728     getParams : function(){
27729         var bp = this.form.baseParams;
27730         var p = this.options.params;
27731         if(p){
27732             if(typeof p == "object"){
27733                 p = Roo.urlEncode(Roo.applyIf(p, bp));
27734             }else if(typeof p == 'string' && bp){
27735                 p += '&' + Roo.urlEncode(bp);
27736             }
27737         }else if(bp){
27738             p = Roo.urlEncode(bp);
27739         }
27740         return p;
27741     },
27742
27743     createCallback : function(){
27744         return {
27745             success: this.success,
27746             failure: this.failure,
27747             scope: this,
27748             timeout: (this.form.timeout*1000),
27749             upload: this.form.fileUpload ? this.success : undefined
27750         };
27751     }
27752 };
27753
27754 Roo.form.Action.Submit = function(form, options){
27755     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
27756 };
27757
27758 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
27759     type : 'submit',
27760
27761     haveProgress : false,
27762     uploadComplete : false,
27763     
27764     // uploadProgress indicator.
27765     uploadProgress : function()
27766     {
27767         if (!this.form.progressUrl) {
27768             return;
27769         }
27770         
27771         if (!this.haveProgress) {
27772             Roo.MessageBox.progress("Uploading", "Uploading");
27773         }
27774         if (this.uploadComplete) {
27775            Roo.MessageBox.hide();
27776            return;
27777         }
27778         
27779         this.haveProgress = true;
27780    
27781         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
27782         
27783         var c = new Roo.data.Connection();
27784         c.request({
27785             url : this.form.progressUrl,
27786             params: {
27787                 id : uid
27788             },
27789             method: 'GET',
27790             success : function(req){
27791                //console.log(data);
27792                 var rdata = false;
27793                 var edata;
27794                 try  {
27795                    rdata = Roo.decode(req.responseText)
27796                 } catch (e) {
27797                     Roo.log("Invalid data from server..");
27798                     Roo.log(edata);
27799                     return;
27800                 }
27801                 if (!rdata || !rdata.success) {
27802                     Roo.log(rdata);
27803                     return;
27804                 }
27805                 var data = rdata.data;
27806                 
27807                 if (this.uploadComplete) {
27808                    Roo.MessageBox.hide();
27809                    return;
27810                 }
27811                    
27812                 if (data){
27813                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
27814                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
27815                     );
27816                 }
27817                 this.uploadProgress.defer(2000,this);
27818             },
27819        
27820             failure: function(data) {
27821                 Roo.log('progress url failed ');
27822                 Roo.log(data);
27823             },
27824             scope : this
27825         });
27826            
27827     },
27828     
27829     
27830     run : function()
27831     {
27832         // run get Values on the form, so it syncs any secondary forms.
27833         this.form.getValues();
27834         
27835         var o = this.options;
27836         var method = this.getMethod();
27837         var isPost = method == 'POST';
27838         if(o.clientValidation === false || this.form.isValid()){
27839             
27840             if (this.form.progressUrl) {
27841                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
27842                     (new Date() * 1) + '' + Math.random());
27843                     
27844             } 
27845             
27846             
27847             Roo.Ajax.request(Roo.apply(this.createCallback(), {
27848                 form:this.form.el.dom,
27849                 url:this.getUrl(!isPost),
27850                 method: method,
27851                 params:isPost ? this.getParams() : null,
27852                 isUpload: this.form.fileUpload
27853             }));
27854             
27855             this.uploadProgress();
27856
27857         }else if (o.clientValidation !== false){ // client validation failed
27858             this.failureType = Roo.form.Action.CLIENT_INVALID;
27859             this.form.afterAction(this, false);
27860         }
27861     },
27862
27863     success : function(response)
27864     {
27865         this.uploadComplete= true;
27866         if (this.haveProgress) {
27867             Roo.MessageBox.hide();
27868         }
27869         
27870         
27871         var result = this.processResponse(response);
27872         if(result === true || result.success){
27873             this.form.afterAction(this, true);
27874             return;
27875         }
27876         if(result.errors){
27877             this.form.markInvalid(result.errors);
27878             this.failureType = Roo.form.Action.SERVER_INVALID;
27879         }
27880         this.form.afterAction(this, false);
27881     },
27882     failure : function(response)
27883     {
27884         this.uploadComplete= true;
27885         if (this.haveProgress) {
27886             Roo.MessageBox.hide();
27887         }
27888         
27889         this.response = response;
27890         this.failureType = Roo.form.Action.CONNECT_FAILURE;
27891         this.form.afterAction(this, false);
27892     },
27893     
27894     handleResponse : function(response){
27895         if(this.form.errorReader){
27896             var rs = this.form.errorReader.read(response);
27897             var errors = [];
27898             if(rs.records){
27899                 for(var i = 0, len = rs.records.length; i < len; i++) {
27900                     var r = rs.records[i];
27901                     errors[i] = r.data;
27902                 }
27903             }
27904             if(errors.length < 1){
27905                 errors = null;
27906             }
27907             return {
27908                 success : rs.success,
27909                 errors : errors
27910             };
27911         }
27912         var ret = false;
27913         try {
27914             ret = Roo.decode(response.responseText);
27915         } catch (e) {
27916             ret = {
27917                 success: false,
27918                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
27919                 errors : []
27920             };
27921         }
27922         return ret;
27923         
27924     }
27925 });
27926
27927
27928 Roo.form.Action.Load = function(form, options){
27929     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
27930     this.reader = this.form.reader;
27931 };
27932
27933 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
27934     type : 'load',
27935
27936     run : function(){
27937         
27938         Roo.Ajax.request(Roo.apply(
27939                 this.createCallback(), {
27940                     method:this.getMethod(),
27941                     url:this.getUrl(false),
27942                     params:this.getParams()
27943         }));
27944     },
27945
27946     success : function(response){
27947         
27948         var result = this.processResponse(response);
27949         if(result === true || !result.success || !result.data){
27950             this.failureType = Roo.form.Action.LOAD_FAILURE;
27951             this.form.afterAction(this, false);
27952             return;
27953         }
27954         this.form.clearInvalid();
27955         this.form.setValues(result.data);
27956         this.form.afterAction(this, true);
27957     },
27958
27959     handleResponse : function(response){
27960         if(this.form.reader){
27961             var rs = this.form.reader.read(response);
27962             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
27963             return {
27964                 success : rs.success,
27965                 data : data
27966             };
27967         }
27968         return Roo.decode(response.responseText);
27969     }
27970 });
27971
27972 Roo.form.Action.ACTION_TYPES = {
27973     'load' : Roo.form.Action.Load,
27974     'submit' : Roo.form.Action.Submit
27975 };/*
27976  * Based on:
27977  * Ext JS Library 1.1.1
27978  * Copyright(c) 2006-2007, Ext JS, LLC.
27979  *
27980  * Originally Released Under LGPL - original licence link has changed is not relivant.
27981  *
27982  * Fork - LGPL
27983  * <script type="text/javascript">
27984  */
27985  
27986 /**
27987  * @class Roo.form.Layout
27988  * @extends Roo.Component
27989  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
27990  * @constructor
27991  * @param {Object} config Configuration options
27992  */
27993 Roo.form.Layout = function(config){
27994     var xitems = [];
27995     if (config.items) {
27996         xitems = config.items;
27997         delete config.items;
27998     }
27999     Roo.form.Layout.superclass.constructor.call(this, config);
28000     this.stack = [];
28001     Roo.each(xitems, this.addxtype, this);
28002      
28003 };
28004
28005 Roo.extend(Roo.form.Layout, Roo.Component, {
28006     /**
28007      * @cfg {String/Object} autoCreate
28008      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
28009      */
28010     /**
28011      * @cfg {String/Object/Function} style
28012      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
28013      * a function which returns such a specification.
28014      */
28015     /**
28016      * @cfg {String} labelAlign
28017      * Valid values are "left," "top" and "right" (defaults to "left")
28018      */
28019     /**
28020      * @cfg {Number} labelWidth
28021      * Fixed width in pixels of all field labels (defaults to undefined)
28022      */
28023     /**
28024      * @cfg {Boolean} clear
28025      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
28026      */
28027     clear : true,
28028     /**
28029      * @cfg {String} labelSeparator
28030      * The separator to use after field labels (defaults to ':')
28031      */
28032     labelSeparator : ':',
28033     /**
28034      * @cfg {Boolean} hideLabels
28035      * True to suppress the display of field labels in this layout (defaults to false)
28036      */
28037     hideLabels : false,
28038
28039     // private
28040     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
28041     
28042     isLayout : true,
28043     
28044     // private
28045     onRender : function(ct, position){
28046         if(this.el){ // from markup
28047             this.el = Roo.get(this.el);
28048         }else {  // generate
28049             var cfg = this.getAutoCreate();
28050             this.el = ct.createChild(cfg, position);
28051         }
28052         if(this.style){
28053             this.el.applyStyles(this.style);
28054         }
28055         if(this.labelAlign){
28056             this.el.addClass('x-form-label-'+this.labelAlign);
28057         }
28058         if(this.hideLabels){
28059             this.labelStyle = "display:none";
28060             this.elementStyle = "padding-left:0;";
28061         }else{
28062             if(typeof this.labelWidth == 'number'){
28063                 this.labelStyle = "width:"+this.labelWidth+"px;";
28064                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
28065             }
28066             if(this.labelAlign == 'top'){
28067                 this.labelStyle = "width:auto;";
28068                 this.elementStyle = "padding-left:0;";
28069             }
28070         }
28071         var stack = this.stack;
28072         var slen = stack.length;
28073         if(slen > 0){
28074             if(!this.fieldTpl){
28075                 var t = new Roo.Template(
28076                     '<div class="x-form-item {5}">',
28077                         '<label for="{0}" style="{2}">{1}{4}</label>',
28078                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
28079                         '</div>',
28080                     '</div><div class="x-form-clear-left"></div>'
28081                 );
28082                 t.disableFormats = true;
28083                 t.compile();
28084                 Roo.form.Layout.prototype.fieldTpl = t;
28085             }
28086             for(var i = 0; i < slen; i++) {
28087                 if(stack[i].isFormField){
28088                     this.renderField(stack[i]);
28089                 }else{
28090                     this.renderComponent(stack[i]);
28091                 }
28092             }
28093         }
28094         if(this.clear){
28095             this.el.createChild({cls:'x-form-clear'});
28096         }
28097     },
28098
28099     // private
28100     renderField : function(f){
28101         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
28102                f.id, //0
28103                f.fieldLabel, //1
28104                f.labelStyle||this.labelStyle||'', //2
28105                this.elementStyle||'', //3
28106                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
28107                f.itemCls||this.itemCls||''  //5
28108        ], true).getPrevSibling());
28109     },
28110
28111     // private
28112     renderComponent : function(c){
28113         c.render(c.isLayout ? this.el : this.el.createChild());    
28114     },
28115     /**
28116      * Adds a object form elements (using the xtype property as the factory method.)
28117      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
28118      * @param {Object} config 
28119      */
28120     addxtype : function(o)
28121     {
28122         // create the lement.
28123         o.form = this.form;
28124         var fe = Roo.factory(o, Roo.form);
28125         this.form.allItems.push(fe);
28126         this.stack.push(fe);
28127         
28128         if (fe.isFormField) {
28129             this.form.items.add(fe);
28130         }
28131          
28132         return fe;
28133     }
28134 });
28135
28136 /**
28137  * @class Roo.form.Column
28138  * @extends Roo.form.Layout
28139  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
28140  * @constructor
28141  * @param {Object} config Configuration options
28142  */
28143 Roo.form.Column = function(config){
28144     Roo.form.Column.superclass.constructor.call(this, config);
28145 };
28146
28147 Roo.extend(Roo.form.Column, Roo.form.Layout, {
28148     /**
28149      * @cfg {Number/String} width
28150      * The fixed width of the column in pixels or CSS value (defaults to "auto")
28151      */
28152     /**
28153      * @cfg {String/Object} autoCreate
28154      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
28155      */
28156
28157     // private
28158     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
28159
28160     // private
28161     onRender : function(ct, position){
28162         Roo.form.Column.superclass.onRender.call(this, ct, position);
28163         if(this.width){
28164             this.el.setWidth(this.width);
28165         }
28166     }
28167 });
28168
28169
28170 /**
28171  * @class Roo.form.Row
28172  * @extends Roo.form.Layout
28173  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
28174  * @constructor
28175  * @param {Object} config Configuration options
28176  */
28177
28178  
28179 Roo.form.Row = function(config){
28180     Roo.form.Row.superclass.constructor.call(this, config);
28181 };
28182  
28183 Roo.extend(Roo.form.Row, Roo.form.Layout, {
28184       /**
28185      * @cfg {Number/String} width
28186      * The fixed width of the column in pixels or CSS value (defaults to "auto")
28187      */
28188     /**
28189      * @cfg {Number/String} height
28190      * The fixed height of the column in pixels or CSS value (defaults to "auto")
28191      */
28192     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
28193     
28194     padWidth : 20,
28195     // private
28196     onRender : function(ct, position){
28197         //console.log('row render');
28198         if(!this.rowTpl){
28199             var t = new Roo.Template(
28200                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
28201                     '<label for="{0}" style="{2}">{1}{4}</label>',
28202                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
28203                     '</div>',
28204                 '</div>'
28205             );
28206             t.disableFormats = true;
28207             t.compile();
28208             Roo.form.Layout.prototype.rowTpl = t;
28209         }
28210         this.fieldTpl = this.rowTpl;
28211         
28212         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
28213         var labelWidth = 100;
28214         
28215         if ((this.labelAlign != 'top')) {
28216             if (typeof this.labelWidth == 'number') {
28217                 labelWidth = this.labelWidth
28218             }
28219             this.padWidth =  20 + labelWidth;
28220             
28221         }
28222         
28223         Roo.form.Column.superclass.onRender.call(this, ct, position);
28224         if(this.width){
28225             this.el.setWidth(this.width);
28226         }
28227         if(this.height){
28228             this.el.setHeight(this.height);
28229         }
28230     },
28231     
28232     // private
28233     renderField : function(f){
28234         f.fieldEl = this.fieldTpl.append(this.el, [
28235                f.id, f.fieldLabel,
28236                f.labelStyle||this.labelStyle||'',
28237                this.elementStyle||'',
28238                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
28239                f.itemCls||this.itemCls||'',
28240                f.width ? f.width + this.padWidth : 160 + this.padWidth
28241        ],true);
28242     }
28243 });
28244  
28245
28246 /**
28247  * @class Roo.form.FieldSet
28248  * @extends Roo.form.Layout
28249  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
28250  * @constructor
28251  * @param {Object} config Configuration options
28252  */
28253 Roo.form.FieldSet = function(config){
28254     Roo.form.FieldSet.superclass.constructor.call(this, config);
28255 };
28256
28257 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
28258     /**
28259      * @cfg {String} legend
28260      * The text to display as the legend for the FieldSet (defaults to '')
28261      */
28262     /**
28263      * @cfg {String/Object} autoCreate
28264      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
28265      */
28266
28267     // private
28268     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
28269
28270     // private
28271     onRender : function(ct, position){
28272         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
28273         if(this.legend){
28274             this.setLegend(this.legend);
28275         }
28276     },
28277
28278     // private
28279     setLegend : function(text){
28280         if(this.rendered){
28281             this.el.child('legend').update(text);
28282         }
28283     }
28284 });/*
28285  * Based on:
28286  * Ext JS Library 1.1.1
28287  * Copyright(c) 2006-2007, Ext JS, LLC.
28288  *
28289  * Originally Released Under LGPL - original licence link has changed is not relivant.
28290  *
28291  * Fork - LGPL
28292  * <script type="text/javascript">
28293  */
28294 /**
28295  * @class Roo.form.VTypes
28296  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
28297  * @singleton
28298  */
28299 Roo.form.VTypes = function(){
28300     // closure these in so they are only created once.
28301     var alpha = /^[a-zA-Z_]+$/;
28302     var alphanum = /^[a-zA-Z0-9_]+$/;
28303     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
28304     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
28305
28306     // All these messages and functions are configurable
28307     return {
28308         /**
28309          * The function used to validate email addresses
28310          * @param {String} value The email address
28311          */
28312         'email' : function(v){
28313             return email.test(v);
28314         },
28315         /**
28316          * The error text to display when the email validation function returns false
28317          * @type String
28318          */
28319         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
28320         /**
28321          * The keystroke filter mask to be applied on email input
28322          * @type RegExp
28323          */
28324         'emailMask' : /[a-z0-9_\.\-@]/i,
28325
28326         /**
28327          * The function used to validate URLs
28328          * @param {String} value The URL
28329          */
28330         'url' : function(v){
28331             return url.test(v);
28332         },
28333         /**
28334          * The error text to display when the url validation function returns false
28335          * @type String
28336          */
28337         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
28338         
28339         /**
28340          * The function used to validate alpha values
28341          * @param {String} value The value
28342          */
28343         'alpha' : function(v){
28344             return alpha.test(v);
28345         },
28346         /**
28347          * The error text to display when the alpha validation function returns false
28348          * @type String
28349          */
28350         'alphaText' : 'This field should only contain letters and _',
28351         /**
28352          * The keystroke filter mask to be applied on alpha input
28353          * @type RegExp
28354          */
28355         'alphaMask' : /[a-z_]/i,
28356
28357         /**
28358          * The function used to validate alphanumeric values
28359          * @param {String} value The value
28360          */
28361         'alphanum' : function(v){
28362             return alphanum.test(v);
28363         },
28364         /**
28365          * The error text to display when the alphanumeric validation function returns false
28366          * @type String
28367          */
28368         'alphanumText' : 'This field should only contain letters, numbers and _',
28369         /**
28370          * The keystroke filter mask to be applied on alphanumeric input
28371          * @type RegExp
28372          */
28373         'alphanumMask' : /[a-z0-9_]/i
28374     };
28375 }();//<script type="text/javascript">
28376
28377 /**
28378  * @class Roo.form.FCKeditor
28379  * @extends Roo.form.TextArea
28380  * Wrapper around the FCKEditor http://www.fckeditor.net
28381  * @constructor
28382  * Creates a new FCKeditor
28383  * @param {Object} config Configuration options
28384  */
28385 Roo.form.FCKeditor = function(config){
28386     Roo.form.FCKeditor.superclass.constructor.call(this, config);
28387     this.addEvents({
28388          /**
28389          * @event editorinit
28390          * Fired when the editor is initialized - you can add extra handlers here..
28391          * @param {FCKeditor} this
28392          * @param {Object} the FCK object.
28393          */
28394         editorinit : true
28395     });
28396     
28397     
28398 };
28399 Roo.form.FCKeditor.editors = { };
28400 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
28401 {
28402     //defaultAutoCreate : {
28403     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
28404     //},
28405     // private
28406     /**
28407      * @cfg {Object} fck options - see fck manual for details.
28408      */
28409     fckconfig : false,
28410     
28411     /**
28412      * @cfg {Object} fck toolbar set (Basic or Default)
28413      */
28414     toolbarSet : 'Basic',
28415     /**
28416      * @cfg {Object} fck BasePath
28417      */ 
28418     basePath : '/fckeditor/',
28419     
28420     
28421     frame : false,
28422     
28423     value : '',
28424     
28425    
28426     onRender : function(ct, position)
28427     {
28428         if(!this.el){
28429             this.defaultAutoCreate = {
28430                 tag: "textarea",
28431                 style:"width:300px;height:60px;",
28432                 autocomplete: "off"
28433             };
28434         }
28435         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
28436         /*
28437         if(this.grow){
28438             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
28439             if(this.preventScrollbars){
28440                 this.el.setStyle("overflow", "hidden");
28441             }
28442             this.el.setHeight(this.growMin);
28443         }
28444         */
28445         //console.log('onrender' + this.getId() );
28446         Roo.form.FCKeditor.editors[this.getId()] = this;
28447          
28448
28449         this.replaceTextarea() ;
28450         
28451     },
28452     
28453     getEditor : function() {
28454         return this.fckEditor;
28455     },
28456     /**
28457      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
28458      * @param {Mixed} value The value to set
28459      */
28460     
28461     
28462     setValue : function(value)
28463     {
28464         //console.log('setValue: ' + value);
28465         
28466         if(typeof(value) == 'undefined') { // not sure why this is happending...
28467             return;
28468         }
28469         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
28470         
28471         //if(!this.el || !this.getEditor()) {
28472         //    this.value = value;
28473             //this.setValue.defer(100,this,[value]);    
28474         //    return;
28475         //} 
28476         
28477         if(!this.getEditor()) {
28478             return;
28479         }
28480         
28481         this.getEditor().SetData(value);
28482         
28483         //
28484
28485     },
28486
28487     /**
28488      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
28489      * @return {Mixed} value The field value
28490      */
28491     getValue : function()
28492     {
28493         
28494         if (this.frame && this.frame.dom.style.display == 'none') {
28495             return Roo.form.FCKeditor.superclass.getValue.call(this);
28496         }
28497         
28498         if(!this.el || !this.getEditor()) {
28499            
28500            // this.getValue.defer(100,this); 
28501             return this.value;
28502         }
28503        
28504         
28505         var value=this.getEditor().GetData();
28506         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
28507         return Roo.form.FCKeditor.superclass.getValue.call(this);
28508         
28509
28510     },
28511
28512     /**
28513      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
28514      * @return {Mixed} value The field value
28515      */
28516     getRawValue : function()
28517     {
28518         if (this.frame && this.frame.dom.style.display == 'none') {
28519             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
28520         }
28521         
28522         if(!this.el || !this.getEditor()) {
28523             //this.getRawValue.defer(100,this); 
28524             return this.value;
28525             return;
28526         }
28527         
28528         
28529         
28530         var value=this.getEditor().GetData();
28531         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
28532         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
28533          
28534     },
28535     
28536     setSize : function(w,h) {
28537         
28538         
28539         
28540         //if (this.frame && this.frame.dom.style.display == 'none') {
28541         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
28542         //    return;
28543         //}
28544         //if(!this.el || !this.getEditor()) {
28545         //    this.setSize.defer(100,this, [w,h]); 
28546         //    return;
28547         //}
28548         
28549         
28550         
28551         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
28552         
28553         this.frame.dom.setAttribute('width', w);
28554         this.frame.dom.setAttribute('height', h);
28555         this.frame.setSize(w,h);
28556         
28557     },
28558     
28559     toggleSourceEdit : function(value) {
28560         
28561       
28562          
28563         this.el.dom.style.display = value ? '' : 'none';
28564         this.frame.dom.style.display = value ?  'none' : '';
28565         
28566     },
28567     
28568     
28569     focus: function(tag)
28570     {
28571         if (this.frame.dom.style.display == 'none') {
28572             return Roo.form.FCKeditor.superclass.focus.call(this);
28573         }
28574         if(!this.el || !this.getEditor()) {
28575             this.focus.defer(100,this, [tag]); 
28576             return;
28577         }
28578         
28579         
28580         
28581         
28582         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
28583         this.getEditor().Focus();
28584         if (tgs.length) {
28585             if (!this.getEditor().Selection.GetSelection()) {
28586                 this.focus.defer(100,this, [tag]); 
28587                 return;
28588             }
28589             
28590             
28591             var r = this.getEditor().EditorDocument.createRange();
28592             r.setStart(tgs[0],0);
28593             r.setEnd(tgs[0],0);
28594             this.getEditor().Selection.GetSelection().removeAllRanges();
28595             this.getEditor().Selection.GetSelection().addRange(r);
28596             this.getEditor().Focus();
28597         }
28598         
28599     },
28600     
28601     
28602     
28603     replaceTextarea : function()
28604     {
28605         if ( document.getElementById( this.getId() + '___Frame' ) )
28606             return ;
28607         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
28608         //{
28609             // We must check the elements firstly using the Id and then the name.
28610         var oTextarea = document.getElementById( this.getId() );
28611         
28612         var colElementsByName = document.getElementsByName( this.getId() ) ;
28613          
28614         oTextarea.style.display = 'none' ;
28615
28616         if ( oTextarea.tabIndex ) {            
28617             this.TabIndex = oTextarea.tabIndex ;
28618         }
28619         
28620         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
28621         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
28622         this.frame = Roo.get(this.getId() + '___Frame')
28623     },
28624     
28625     _getConfigHtml : function()
28626     {
28627         var sConfig = '' ;
28628
28629         for ( var o in this.fckconfig ) {
28630             sConfig += sConfig.length > 0  ? '&amp;' : '';
28631             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
28632         }
28633
28634         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
28635     },
28636     
28637     
28638     _getIFrameHtml : function()
28639     {
28640         var sFile = 'fckeditor.html' ;
28641         /* no idea what this is about..
28642         try
28643         {
28644             if ( (/fcksource=true/i).test( window.top.location.search ) )
28645                 sFile = 'fckeditor.original.html' ;
28646         }
28647         catch (e) { 
28648         */
28649
28650         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
28651         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
28652         
28653         
28654         var html = '<iframe id="' + this.getId() +
28655             '___Frame" src="' + sLink +
28656             '" width="' + this.width +
28657             '" height="' + this.height + '"' +
28658             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
28659             ' frameborder="0" scrolling="no"></iframe>' ;
28660
28661         return html ;
28662     },
28663     
28664     _insertHtmlBefore : function( html, element )
28665     {
28666         if ( element.insertAdjacentHTML )       {
28667             // IE
28668             element.insertAdjacentHTML( 'beforeBegin', html ) ;
28669         } else { // Gecko
28670             var oRange = document.createRange() ;
28671             oRange.setStartBefore( element ) ;
28672             var oFragment = oRange.createContextualFragment( html );
28673             element.parentNode.insertBefore( oFragment, element ) ;
28674         }
28675     }
28676     
28677     
28678   
28679     
28680     
28681     
28682     
28683
28684 });
28685
28686 //Roo.reg('fckeditor', Roo.form.FCKeditor);
28687
28688 function FCKeditor_OnComplete(editorInstance){
28689     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
28690     f.fckEditor = editorInstance;
28691     //console.log("loaded");
28692     f.fireEvent('editorinit', f, editorInstance);
28693
28694   
28695
28696  
28697
28698
28699
28700
28701
28702
28703
28704
28705
28706
28707
28708
28709
28710
28711
28712 //<script type="text/javascript">
28713 /**
28714  * @class Roo.form.GridField
28715  * @extends Roo.form.Field
28716  * Embed a grid (or editable grid into a form)
28717  * STATUS ALPHA
28718  * 
28719  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
28720  * it needs 
28721  * xgrid.store = Roo.data.Store
28722  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
28723  * xgrid.store.reader = Roo.data.JsonReader 
28724  * 
28725  * 
28726  * @constructor
28727  * Creates a new GridField
28728  * @param {Object} config Configuration options
28729  */
28730 Roo.form.GridField = function(config){
28731     Roo.form.GridField.superclass.constructor.call(this, config);
28732      
28733 };
28734
28735 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
28736     /**
28737      * @cfg {Number} width  - used to restrict width of grid..
28738      */
28739     width : 100,
28740     /**
28741      * @cfg {Number} height - used to restrict height of grid..
28742      */
28743     height : 50,
28744      /**
28745      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
28746          * 
28747          *}
28748      */
28749     xgrid : false, 
28750     /**
28751      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
28752      * {tag: "input", type: "checkbox", autocomplete: "off"})
28753      */
28754    // defaultAutoCreate : { tag: 'div' },
28755     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
28756     /**
28757      * @cfg {String} addTitle Text to include for adding a title.
28758      */
28759     addTitle : false,
28760     //
28761     onResize : function(){
28762         Roo.form.Field.superclass.onResize.apply(this, arguments);
28763     },
28764
28765     initEvents : function(){
28766         // Roo.form.Checkbox.superclass.initEvents.call(this);
28767         // has no events...
28768        
28769     },
28770
28771
28772     getResizeEl : function(){
28773         return this.wrap;
28774     },
28775
28776     getPositionEl : function(){
28777         return this.wrap;
28778     },
28779
28780     // private
28781     onRender : function(ct, position){
28782         
28783         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
28784         var style = this.style;
28785         delete this.style;
28786         
28787         Roo.form.GridField.superclass.onRender.call(this, ct, position);
28788         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
28789         this.viewEl = this.wrap.createChild({ tag: 'div' });
28790         if (style) {
28791             this.viewEl.applyStyles(style);
28792         }
28793         if (this.width) {
28794             this.viewEl.setWidth(this.width);
28795         }
28796         if (this.height) {
28797             this.viewEl.setHeight(this.height);
28798         }
28799         //if(this.inputValue !== undefined){
28800         //this.setValue(this.value);
28801         
28802         
28803         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
28804         
28805         
28806         this.grid.render();
28807         this.grid.getDataSource().on('remove', this.refreshValue, this);
28808         this.grid.getDataSource().on('update', this.refreshValue, this);
28809         this.grid.on('afteredit', this.refreshValue, this);
28810  
28811     },
28812      
28813     
28814     /**
28815      * Sets the value of the item. 
28816      * @param {String} either an object  or a string..
28817      */
28818     setValue : function(v){
28819         //this.value = v;
28820         v = v || []; // empty set..
28821         // this does not seem smart - it really only affects memoryproxy grids..
28822         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
28823             var ds = this.grid.getDataSource();
28824             // assumes a json reader..
28825             var data = {}
28826             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
28827             ds.loadData( data);
28828         }
28829         // clear selection so it does not get stale.
28830         if (this.grid.sm) { 
28831             this.grid.sm.clearSelections();
28832         }
28833         
28834         Roo.form.GridField.superclass.setValue.call(this, v);
28835         this.refreshValue();
28836         // should load data in the grid really....
28837     },
28838     
28839     // private
28840     refreshValue: function() {
28841          var val = [];
28842         this.grid.getDataSource().each(function(r) {
28843             val.push(r.data);
28844         });
28845         this.el.dom.value = Roo.encode(val);
28846     }
28847     
28848      
28849     
28850     
28851 });/*
28852  * Based on:
28853  * Ext JS Library 1.1.1
28854  * Copyright(c) 2006-2007, Ext JS, LLC.
28855  *
28856  * Originally Released Under LGPL - original licence link has changed is not relivant.
28857  *
28858  * Fork - LGPL
28859  * <script type="text/javascript">
28860  */
28861 /**
28862  * @class Roo.form.DisplayField
28863  * @extends Roo.form.Field
28864  * A generic Field to display non-editable data.
28865  * @constructor
28866  * Creates a new Display Field item.
28867  * @param {Object} config Configuration options
28868  */
28869 Roo.form.DisplayField = function(config){
28870     Roo.form.DisplayField.superclass.constructor.call(this, config);
28871     
28872 };
28873
28874 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
28875     inputType:      'hidden',
28876     allowBlank:     true,
28877     readOnly:         true,
28878     
28879  
28880     /**
28881      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
28882      */
28883     focusClass : undefined,
28884     /**
28885      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
28886      */
28887     fieldClass: 'x-form-field',
28888     
28889      /**
28890      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
28891      */
28892     valueRenderer: undefined,
28893     
28894     width: 100,
28895     /**
28896      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
28897      * {tag: "input", type: "checkbox", autocomplete: "off"})
28898      */
28899      
28900  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
28901
28902     onResize : function(){
28903         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
28904         
28905     },
28906
28907     initEvents : function(){
28908         // Roo.form.Checkbox.superclass.initEvents.call(this);
28909         // has no events...
28910        
28911     },
28912
28913
28914     getResizeEl : function(){
28915         return this.wrap;
28916     },
28917
28918     getPositionEl : function(){
28919         return this.wrap;
28920     },
28921
28922     // private
28923     onRender : function(ct, position){
28924         
28925         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
28926         //if(this.inputValue !== undefined){
28927         this.wrap = this.el.wrap();
28928         
28929         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
28930         
28931         if (this.bodyStyle) {
28932             this.viewEl.applyStyles(this.bodyStyle);
28933         }
28934         //this.viewEl.setStyle('padding', '2px');
28935         
28936         this.setValue(this.value);
28937         
28938     },
28939 /*
28940     // private
28941     initValue : Roo.emptyFn,
28942
28943   */
28944
28945         // private
28946     onClick : function(){
28947         
28948     },
28949
28950     /**
28951      * Sets the checked state of the checkbox.
28952      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
28953      */
28954     setValue : function(v){
28955         this.value = v;
28956         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
28957         // this might be called before we have a dom element..
28958         if (!this.viewEl) {
28959             return;
28960         }
28961         this.viewEl.dom.innerHTML = html;
28962         Roo.form.DisplayField.superclass.setValue.call(this, v);
28963
28964     }
28965 });/*
28966  * 
28967  * Licence- LGPL
28968  * 
28969  */
28970
28971 /**
28972  * @class Roo.form.DayPicker
28973  * @extends Roo.form.Field
28974  * A Day picker show [M] [T] [W] ....
28975  * @constructor
28976  * Creates a new Day Picker
28977  * @param {Object} config Configuration options
28978  */
28979 Roo.form.DayPicker= function(config){
28980     Roo.form.DayPicker.superclass.constructor.call(this, config);
28981      
28982 };
28983
28984 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
28985     /**
28986      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
28987      */
28988     focusClass : undefined,
28989     /**
28990      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
28991      */
28992     fieldClass: "x-form-field",
28993    
28994     /**
28995      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
28996      * {tag: "input", type: "checkbox", autocomplete: "off"})
28997      */
28998     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
28999     
29000    
29001     actionMode : 'viewEl', 
29002     //
29003     // private
29004  
29005     inputType : 'hidden',
29006     
29007      
29008     inputElement: false, // real input element?
29009     basedOn: false, // ????
29010     
29011     isFormField: true, // not sure where this is needed!!!!
29012
29013     onResize : function(){
29014         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
29015         if(!this.boxLabel){
29016             this.el.alignTo(this.wrap, 'c-c');
29017         }
29018     },
29019
29020     initEvents : function(){
29021         Roo.form.Checkbox.superclass.initEvents.call(this);
29022         this.el.on("click", this.onClick,  this);
29023         this.el.on("change", this.onClick,  this);
29024     },
29025
29026
29027     getResizeEl : function(){
29028         return this.wrap;
29029     },
29030
29031     getPositionEl : function(){
29032         return this.wrap;
29033     },
29034
29035     
29036     // private
29037     onRender : function(ct, position){
29038         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
29039        
29040         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
29041         
29042         var r1 = '<table><tr>';
29043         var r2 = '<tr class="x-form-daypick-icons">';
29044         for (var i=0; i < 7; i++) {
29045             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
29046             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
29047         }
29048         
29049         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
29050         viewEl.select('img').on('click', this.onClick, this);
29051         this.viewEl = viewEl;   
29052         
29053         
29054         // this will not work on Chrome!!!
29055         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
29056         this.el.on('propertychange', this.setFromHidden,  this);  //ie
29057         
29058         
29059           
29060
29061     },
29062
29063     // private
29064     initValue : Roo.emptyFn,
29065
29066     /**
29067      * Returns the checked state of the checkbox.
29068      * @return {Boolean} True if checked, else false
29069      */
29070     getValue : function(){
29071         return this.el.dom.value;
29072         
29073     },
29074
29075         // private
29076     onClick : function(e){ 
29077         //this.setChecked(!this.checked);
29078         Roo.get(e.target).toggleClass('x-menu-item-checked');
29079         this.refreshValue();
29080         //if(this.el.dom.checked != this.checked){
29081         //    this.setValue(this.el.dom.checked);
29082        // }
29083     },
29084     
29085     // private
29086     refreshValue : function()
29087     {
29088         var val = '';
29089         this.viewEl.select('img',true).each(function(e,i,n)  {
29090             val += e.is(".x-menu-item-checked") ? String(n) : '';
29091         });
29092         this.setValue(val, true);
29093     },
29094
29095     /**
29096      * Sets the checked state of the checkbox.
29097      * On is always based on a string comparison between inputValue and the param.
29098      * @param {Boolean/String} value - the value to set 
29099      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
29100      */
29101     setValue : function(v,suppressEvent){
29102         if (!this.el.dom) {
29103             return;
29104         }
29105         var old = this.el.dom.value ;
29106         this.el.dom.value = v;
29107         if (suppressEvent) {
29108             return ;
29109         }
29110          
29111         // update display..
29112         this.viewEl.select('img',true).each(function(e,i,n)  {
29113             
29114             var on = e.is(".x-menu-item-checked");
29115             var newv = v.indexOf(String(n)) > -1;
29116             if (on != newv) {
29117                 e.toggleClass('x-menu-item-checked');
29118             }
29119             
29120         });
29121         
29122         
29123         this.fireEvent('change', this, v, old);
29124         
29125         
29126     },
29127    
29128     // handle setting of hidden value by some other method!!?!?
29129     setFromHidden: function()
29130     {
29131         if(!this.el){
29132             return;
29133         }
29134         //console.log("SET FROM HIDDEN");
29135         //alert('setFrom hidden');
29136         this.setValue(this.el.dom.value);
29137     },
29138     
29139     onDestroy : function()
29140     {
29141         if(this.viewEl){
29142             Roo.get(this.viewEl).remove();
29143         }
29144          
29145         Roo.form.DayPicker.superclass.onDestroy.call(this);
29146     }
29147
29148 });/*
29149  * RooJS Library 1.1.1
29150  * Copyright(c) 2008-2011  Alan Knowles
29151  *
29152  * License - LGPL
29153  */
29154  
29155
29156 /**
29157  * @class Roo.form.ComboCheck
29158  * @extends Roo.form.ComboBox
29159  * A combobox for multiple select items.
29160  *
29161  * FIXME - could do with a reset button..
29162  * 
29163  * @constructor
29164  * Create a new ComboCheck
29165  * @param {Object} config Configuration options
29166  */
29167 Roo.form.ComboCheck = function(config){
29168     Roo.form.ComboCheck.superclass.constructor.call(this, config);
29169     // should verify some data...
29170     // like
29171     // hiddenName = required..
29172     // displayField = required
29173     // valudField == required
29174     var req= [ 'hiddenName', 'displayField', 'valueField' ];
29175     var _t = this;
29176     Roo.each(req, function(e) {
29177         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
29178             throw "Roo.form.ComboCheck : missing value for: " + e;
29179         }
29180     });
29181     
29182     
29183 };
29184
29185 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
29186      
29187      
29188     editable : false,
29189      
29190     selectedClass: 'x-menu-item-checked', 
29191     
29192     // private
29193     onRender : function(ct, position){
29194         var _t = this;
29195         
29196         
29197         
29198         if(!this.tpl){
29199             var cls = 'x-combo-list';
29200
29201             
29202             this.tpl =  new Roo.Template({
29203                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
29204                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
29205                    '<span>{' + this.displayField + '}</span>' +
29206                     '</div>' 
29207                 
29208             });
29209         }
29210  
29211         
29212         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
29213         this.view.singleSelect = false;
29214         this.view.multiSelect = true;
29215         this.view.toggleSelect = true;
29216         this.pageTb.add(new Roo.Toolbar.Fill(), {
29217             
29218             text: 'Done',
29219             handler: function()
29220             {
29221                 _t.collapse();
29222             }
29223         });
29224     },
29225     
29226     onViewOver : function(e, t){
29227         // do nothing...
29228         return;
29229         
29230     },
29231     
29232     onViewClick : function(doFocus,index){
29233         return;
29234         
29235     },
29236     select: function () {
29237         //Roo.log("SELECT CALLED");
29238     },
29239      
29240     selectByValue : function(xv, scrollIntoView){
29241         var ar = this.getValueArray();
29242         var sels = [];
29243         
29244         Roo.each(ar, function(v) {
29245             if(v === undefined || v === null){
29246                 return;
29247             }
29248             var r = this.findRecord(this.valueField, v);
29249             if(r){
29250                 sels.push(this.store.indexOf(r))
29251                 
29252             }
29253         },this);
29254         this.view.select(sels);
29255         return false;
29256     },
29257     
29258     
29259     
29260     onSelect : function(record, index){
29261        // Roo.log("onselect Called");
29262        // this is only called by the clear button now..
29263         this.view.clearSelections();
29264         this.setValue('[]');
29265         if (this.value != this.valueBefore) {
29266             this.fireEvent('change', this, this.value, this.valueBefore);
29267         }
29268     },
29269     getValueArray : function()
29270     {
29271         var ar = [] ;
29272         
29273         try {
29274             //Roo.log(this.value);
29275             if (typeof(this.value) == 'undefined') {
29276                 return [];
29277             }
29278             var ar = Roo.decode(this.value);
29279             return  ar instanceof Array ? ar : []; //?? valid?
29280             
29281         } catch(e) {
29282             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
29283             return [];
29284         }
29285          
29286     },
29287     expand : function ()
29288     {
29289         Roo.form.ComboCheck.superclass.expand.call(this);
29290         this.valueBefore = this.value;
29291         
29292
29293     },
29294     
29295     collapse : function(){
29296         Roo.form.ComboCheck.superclass.collapse.call(this);
29297         var sl = this.view.getSelectedIndexes();
29298         var st = this.store;
29299         var nv = [];
29300         var tv = [];
29301         var r;
29302         Roo.each(sl, function(i) {
29303             r = st.getAt(i);
29304             nv.push(r.get(this.valueField));
29305         },this);
29306         this.setValue(Roo.encode(nv));
29307         if (this.value != this.valueBefore) {
29308
29309             this.fireEvent('change', this, this.value, this.valueBefore);
29310         }
29311         
29312     },
29313     
29314     setValue : function(v){
29315         // Roo.log(v);
29316         this.value = v;
29317         
29318         var vals = this.getValueArray();
29319         var tv = [];
29320         Roo.each(vals, function(k) {
29321             var r = this.findRecord(this.valueField, k);
29322             if(r){
29323                 tv.push(r.data[this.displayField]);
29324             }else if(this.valueNotFoundText !== undefined){
29325                 tv.push( this.valueNotFoundText );
29326             }
29327         },this);
29328        // Roo.log(tv);
29329         
29330         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
29331         this.hiddenField.value = v;
29332         this.value = v;
29333     }
29334     
29335 });//<script type="text/javasscript">
29336  
29337
29338 /**
29339  * @class Roo.DDView
29340  * A DnD enabled version of Roo.View.
29341  * @param {Element/String} container The Element in which to create the View.
29342  * @param {String} tpl The template string used to create the markup for each element of the View
29343  * @param {Object} config The configuration properties. These include all the config options of
29344  * {@link Roo.View} plus some specific to this class.<br>
29345  * <p>
29346  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
29347  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
29348  * <p>
29349  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
29350 .x-view-drag-insert-above {
29351         border-top:1px dotted #3366cc;
29352 }
29353 .x-view-drag-insert-below {
29354         border-bottom:1px dotted #3366cc;
29355 }
29356 </code></pre>
29357  * 
29358  */
29359  
29360 Roo.DDView = function(container, tpl, config) {
29361     Roo.DDView.superclass.constructor.apply(this, arguments);
29362     this.getEl().setStyle("outline", "0px none");
29363     this.getEl().unselectable();
29364     if (this.dragGroup) {
29365                 this.setDraggable(this.dragGroup.split(","));
29366     }
29367     if (this.dropGroup) {
29368                 this.setDroppable(this.dropGroup.split(","));
29369     }
29370     if (this.deletable) {
29371         this.setDeletable();
29372     }
29373     this.isDirtyFlag = false;
29374         this.addEvents({
29375                 "drop" : true
29376         });
29377 };
29378
29379 Roo.extend(Roo.DDView, Roo.View, {
29380 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
29381 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
29382 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
29383 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
29384
29385         isFormField: true,
29386
29387         reset: Roo.emptyFn,
29388         
29389         clearInvalid: Roo.form.Field.prototype.clearInvalid,
29390
29391         validate: function() {
29392                 return true;
29393         },
29394         
29395         destroy: function() {
29396                 this.purgeListeners();
29397                 this.getEl.removeAllListeners();
29398                 this.getEl().remove();
29399                 if (this.dragZone) {
29400                         if (this.dragZone.destroy) {
29401                                 this.dragZone.destroy();
29402                         }
29403                 }
29404                 if (this.dropZone) {
29405                         if (this.dropZone.destroy) {
29406                                 this.dropZone.destroy();
29407                         }
29408                 }
29409         },
29410
29411 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
29412         getName: function() {
29413                 return this.name;
29414         },
29415
29416 /**     Loads the View from a JSON string representing the Records to put into the Store. */
29417         setValue: function(v) {
29418                 if (!this.store) {
29419                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
29420                 }
29421                 var data = {};
29422                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
29423                 this.store.proxy = new Roo.data.MemoryProxy(data);
29424                 this.store.load();
29425         },
29426
29427 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
29428         getValue: function() {
29429                 var result = '(';
29430                 this.store.each(function(rec) {
29431                         result += rec.id + ',';
29432                 });
29433                 return result.substr(0, result.length - 1) + ')';
29434         },
29435         
29436         getIds: function() {
29437                 var i = 0, result = new Array(this.store.getCount());
29438                 this.store.each(function(rec) {
29439                         result[i++] = rec.id;
29440                 });
29441                 return result;
29442         },
29443         
29444         isDirty: function() {
29445                 return this.isDirtyFlag;
29446         },
29447
29448 /**
29449  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
29450  *      whole Element becomes the target, and this causes the drop gesture to append.
29451  */
29452     getTargetFromEvent : function(e) {
29453                 var target = e.getTarget();
29454                 while ((target !== null) && (target.parentNode != this.el.dom)) {
29455                 target = target.parentNode;
29456                 }
29457                 if (!target) {
29458                         target = this.el.dom.lastChild || this.el.dom;
29459                 }
29460                 return target;
29461     },
29462
29463 /**
29464  *      Create the drag data which consists of an object which has the property "ddel" as
29465  *      the drag proxy element. 
29466  */
29467     getDragData : function(e) {
29468         var target = this.findItemFromChild(e.getTarget());
29469                 if(target) {
29470                         this.handleSelection(e);
29471                         var selNodes = this.getSelectedNodes();
29472             var dragData = {
29473                 source: this,
29474                 copy: this.copy || (this.allowCopy && e.ctrlKey),
29475                 nodes: selNodes,
29476                 records: []
29477                         };
29478                         var selectedIndices = this.getSelectedIndexes();
29479                         for (var i = 0; i < selectedIndices.length; i++) {
29480                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
29481                         }
29482                         if (selNodes.length == 1) {
29483                                 dragData.ddel = target.cloneNode(true); // the div element
29484                         } else {
29485                                 var div = document.createElement('div'); // create the multi element drag "ghost"
29486                                 div.className = 'multi-proxy';
29487                                 for (var i = 0, len = selNodes.length; i < len; i++) {
29488                                         div.appendChild(selNodes[i].cloneNode(true));
29489                                 }
29490                                 dragData.ddel = div;
29491                         }
29492             //console.log(dragData)
29493             //console.log(dragData.ddel.innerHTML)
29494                         return dragData;
29495                 }
29496         //console.log('nodragData')
29497                 return false;
29498     },
29499     
29500 /**     Specify to which ddGroup items in this DDView may be dragged. */
29501     setDraggable: function(ddGroup) {
29502         if (ddGroup instanceof Array) {
29503                 Roo.each(ddGroup, this.setDraggable, this);
29504                 return;
29505         }
29506         if (this.dragZone) {
29507                 this.dragZone.addToGroup(ddGroup);
29508         } else {
29509                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
29510                                 containerScroll: true,
29511                                 ddGroup: ddGroup 
29512
29513                         });
29514 //                      Draggability implies selection. DragZone's mousedown selects the element.
29515                         if (!this.multiSelect) { this.singleSelect = true; }
29516
29517 //                      Wire the DragZone's handlers up to methods in *this*
29518                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
29519                 }
29520     },
29521
29522 /**     Specify from which ddGroup this DDView accepts drops. */
29523     setDroppable: function(ddGroup) {
29524         if (ddGroup instanceof Array) {
29525                 Roo.each(ddGroup, this.setDroppable, this);
29526                 return;
29527         }
29528         if (this.dropZone) {
29529                 this.dropZone.addToGroup(ddGroup);
29530         } else {
29531                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
29532                                 containerScroll: true,
29533                                 ddGroup: ddGroup
29534                         });
29535
29536 //                      Wire the DropZone's handlers up to methods in *this*
29537                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
29538                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
29539                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
29540                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
29541                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
29542                 }
29543     },
29544
29545 /**     Decide whether to drop above or below a View node. */
29546     getDropPoint : function(e, n, dd){
29547         if (n == this.el.dom) { return "above"; }
29548                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
29549                 var c = t + (b - t) / 2;
29550                 var y = Roo.lib.Event.getPageY(e);
29551                 if(y <= c) {
29552                         return "above";
29553                 }else{
29554                         return "below";
29555                 }
29556     },
29557
29558     onNodeEnter : function(n, dd, e, data){
29559                 return false;
29560     },
29561     
29562     onNodeOver : function(n, dd, e, data){
29563                 var pt = this.getDropPoint(e, n, dd);
29564                 // set the insert point style on the target node
29565                 var dragElClass = this.dropNotAllowed;
29566                 if (pt) {
29567                         var targetElClass;
29568                         if (pt == "above"){
29569                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
29570                                 targetElClass = "x-view-drag-insert-above";
29571                         } else {
29572                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
29573                                 targetElClass = "x-view-drag-insert-below";
29574                         }
29575                         if (this.lastInsertClass != targetElClass){
29576                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
29577                                 this.lastInsertClass = targetElClass;
29578                         }
29579                 }
29580                 return dragElClass;
29581         },
29582
29583     onNodeOut : function(n, dd, e, data){
29584                 this.removeDropIndicators(n);
29585     },
29586
29587     onNodeDrop : function(n, dd, e, data){
29588         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
29589                 return false;
29590         }
29591         var pt = this.getDropPoint(e, n, dd);
29592                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
29593                 if (pt == "below") { insertAt++; }
29594                 for (var i = 0; i < data.records.length; i++) {
29595                         var r = data.records[i];
29596                         var dup = this.store.getById(r.id);
29597                         if (dup && (dd != this.dragZone)) {
29598                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
29599                         } else {
29600                                 if (data.copy) {
29601                                         this.store.insert(insertAt++, r.copy());
29602                                 } else {
29603                                         data.source.isDirtyFlag = true;
29604                                         r.store.remove(r);
29605                                         this.store.insert(insertAt++, r);
29606                                 }
29607                                 this.isDirtyFlag = true;
29608                         }
29609                 }
29610                 this.dragZone.cachedTarget = null;
29611                 return true;
29612     },
29613
29614     removeDropIndicators : function(n){
29615                 if(n){
29616                         Roo.fly(n).removeClass([
29617                                 "x-view-drag-insert-above",
29618                                 "x-view-drag-insert-below"]);
29619                         this.lastInsertClass = "_noclass";
29620                 }
29621     },
29622
29623 /**
29624  *      Utility method. Add a delete option to the DDView's context menu.
29625  *      @param {String} imageUrl The URL of the "delete" icon image.
29626  */
29627         setDeletable: function(imageUrl) {
29628                 if (!this.singleSelect && !this.multiSelect) {
29629                         this.singleSelect = true;
29630                 }
29631                 var c = this.getContextMenu();
29632                 this.contextMenu.on("itemclick", function(item) {
29633                         switch (item.id) {
29634                                 case "delete":
29635                                         this.remove(this.getSelectedIndexes());
29636                                         break;
29637                         }
29638                 }, this);
29639                 this.contextMenu.add({
29640                         icon: imageUrl,
29641                         id: "delete",
29642                         text: 'Delete'
29643                 });
29644         },
29645         
29646 /**     Return the context menu for this DDView. */
29647         getContextMenu: function() {
29648                 if (!this.contextMenu) {
29649 //                      Create the View's context menu
29650                         this.contextMenu = new Roo.menu.Menu({
29651                                 id: this.id + "-contextmenu"
29652                         });
29653                         this.el.on("contextmenu", this.showContextMenu, this);
29654                 }
29655                 return this.contextMenu;
29656         },
29657         
29658         disableContextMenu: function() {
29659                 if (this.contextMenu) {
29660                         this.el.un("contextmenu", this.showContextMenu, this);
29661                 }
29662         },
29663
29664         showContextMenu: function(e, item) {
29665         item = this.findItemFromChild(e.getTarget());
29666                 if (item) {
29667                         e.stopEvent();
29668                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
29669                         this.contextMenu.showAt(e.getXY());
29670             }
29671     },
29672
29673 /**
29674  *      Remove {@link Roo.data.Record}s at the specified indices.
29675  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
29676  */
29677     remove: function(selectedIndices) {
29678                 selectedIndices = [].concat(selectedIndices);
29679                 for (var i = 0; i < selectedIndices.length; i++) {
29680                         var rec = this.store.getAt(selectedIndices[i]);
29681                         this.store.remove(rec);
29682                 }
29683     },
29684
29685 /**
29686  *      Double click fires the event, but also, if this is draggable, and there is only one other
29687  *      related DropZone, it transfers the selected node.
29688  */
29689     onDblClick : function(e){
29690         var item = this.findItemFromChild(e.getTarget());
29691         if(item){
29692             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
29693                 return false;
29694             }
29695             if (this.dragGroup) {
29696                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
29697                     while (targets.indexOf(this.dropZone) > -1) {
29698                             targets.remove(this.dropZone);
29699                                 }
29700                     if (targets.length == 1) {
29701                                         this.dragZone.cachedTarget = null;
29702                         var el = Roo.get(targets[0].getEl());
29703                         var box = el.getBox(true);
29704                         targets[0].onNodeDrop(el.dom, {
29705                                 target: el.dom,
29706                                 xy: [box.x, box.y + box.height - 1]
29707                         }, null, this.getDragData(e));
29708                     }
29709                 }
29710         }
29711     },
29712     
29713     handleSelection: function(e) {
29714                 this.dragZone.cachedTarget = null;
29715         var item = this.findItemFromChild(e.getTarget());
29716         if (!item) {
29717                 this.clearSelections(true);
29718                 return;
29719         }
29720                 if (item && (this.multiSelect || this.singleSelect)){
29721                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
29722                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
29723                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
29724                                 this.unselect(item);
29725                         } else {
29726                                 this.select(item, this.multiSelect && e.ctrlKey);
29727                                 this.lastSelection = item;
29728                         }
29729                 }
29730     },
29731
29732     onItemClick : function(item, index, e){
29733                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
29734                         return false;
29735                 }
29736                 return true;
29737     },
29738
29739     unselect : function(nodeInfo, suppressEvent){
29740                 var node = this.getNode(nodeInfo);
29741                 if(node && this.isSelected(node)){
29742                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
29743                                 Roo.fly(node).removeClass(this.selectedClass);
29744                                 this.selections.remove(node);
29745                                 if(!suppressEvent){
29746                                         this.fireEvent("selectionchange", this, this.selections);
29747                                 }
29748                         }
29749                 }
29750     }
29751 });
29752 /*
29753  * Based on:
29754  * Ext JS Library 1.1.1
29755  * Copyright(c) 2006-2007, Ext JS, LLC.
29756  *
29757  * Originally Released Under LGPL - original licence link has changed is not relivant.
29758  *
29759  * Fork - LGPL
29760  * <script type="text/javascript">
29761  */
29762  
29763 /**
29764  * @class Roo.LayoutManager
29765  * @extends Roo.util.Observable
29766  * Base class for layout managers.
29767  */
29768 Roo.LayoutManager = function(container, config){
29769     Roo.LayoutManager.superclass.constructor.call(this);
29770     this.el = Roo.get(container);
29771     // ie scrollbar fix
29772     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
29773         document.body.scroll = "no";
29774     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
29775         this.el.position('relative');
29776     }
29777     this.id = this.el.id;
29778     this.el.addClass("x-layout-container");
29779     /** false to disable window resize monitoring @type Boolean */
29780     this.monitorWindowResize = true;
29781     this.regions = {};
29782     this.addEvents({
29783         /**
29784          * @event layout
29785          * Fires when a layout is performed. 
29786          * @param {Roo.LayoutManager} this
29787          */
29788         "layout" : true,
29789         /**
29790          * @event regionresized
29791          * Fires when the user resizes a region. 
29792          * @param {Roo.LayoutRegion} region The resized region
29793          * @param {Number} newSize The new size (width for east/west, height for north/south)
29794          */
29795         "regionresized" : true,
29796         /**
29797          * @event regioncollapsed
29798          * Fires when a region is collapsed. 
29799          * @param {Roo.LayoutRegion} region The collapsed region
29800          */
29801         "regioncollapsed" : true,
29802         /**
29803          * @event regionexpanded
29804          * Fires when a region is expanded.  
29805          * @param {Roo.LayoutRegion} region The expanded region
29806          */
29807         "regionexpanded" : true
29808     });
29809     this.updating = false;
29810     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
29811 };
29812
29813 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
29814     /**
29815      * Returns true if this layout is currently being updated
29816      * @return {Boolean}
29817      */
29818     isUpdating : function(){
29819         return this.updating; 
29820     },
29821     
29822     /**
29823      * Suspend the LayoutManager from doing auto-layouts while
29824      * making multiple add or remove calls
29825      */
29826     beginUpdate : function(){
29827         this.updating = true;    
29828     },
29829     
29830     /**
29831      * Restore auto-layouts and optionally disable the manager from performing a layout
29832      * @param {Boolean} noLayout true to disable a layout update 
29833      */
29834     endUpdate : function(noLayout){
29835         this.updating = false;
29836         if(!noLayout){
29837             this.layout();
29838         }    
29839     },
29840     
29841     layout: function(){
29842         
29843     },
29844     
29845     onRegionResized : function(region, newSize){
29846         this.fireEvent("regionresized", region, newSize);
29847         this.layout();
29848     },
29849     
29850     onRegionCollapsed : function(region){
29851         this.fireEvent("regioncollapsed", region);
29852     },
29853     
29854     onRegionExpanded : function(region){
29855         this.fireEvent("regionexpanded", region);
29856     },
29857         
29858     /**
29859      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
29860      * performs box-model adjustments.
29861      * @return {Object} The size as an object {width: (the width), height: (the height)}
29862      */
29863     getViewSize : function(){
29864         var size;
29865         if(this.el.dom != document.body){
29866             size = this.el.getSize();
29867         }else{
29868             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
29869         }
29870         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
29871         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
29872         return size;
29873     },
29874     
29875     /**
29876      * Returns the Element this layout is bound to.
29877      * @return {Roo.Element}
29878      */
29879     getEl : function(){
29880         return this.el;
29881     },
29882     
29883     /**
29884      * Returns the specified region.
29885      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
29886      * @return {Roo.LayoutRegion}
29887      */
29888     getRegion : function(target){
29889         return this.regions[target.toLowerCase()];
29890     },
29891     
29892     onWindowResize : function(){
29893         if(this.monitorWindowResize){
29894             this.layout();
29895         }
29896     }
29897 });/*
29898  * Based on:
29899  * Ext JS Library 1.1.1
29900  * Copyright(c) 2006-2007, Ext JS, LLC.
29901  *
29902  * Originally Released Under LGPL - original licence link has changed is not relivant.
29903  *
29904  * Fork - LGPL
29905  * <script type="text/javascript">
29906  */
29907 /**
29908  * @class Roo.BorderLayout
29909  * @extends Roo.LayoutManager
29910  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
29911  * please see: <br><br>
29912  * <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>
29913  * <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>
29914  * Example:
29915  <pre><code>
29916  var layout = new Roo.BorderLayout(document.body, {
29917     north: {
29918         initialSize: 25,
29919         titlebar: false
29920     },
29921     west: {
29922         split:true,
29923         initialSize: 200,
29924         minSize: 175,
29925         maxSize: 400,
29926         titlebar: true,
29927         collapsible: true
29928     },
29929     east: {
29930         split:true,
29931         initialSize: 202,
29932         minSize: 175,
29933         maxSize: 400,
29934         titlebar: true,
29935         collapsible: true
29936     },
29937     south: {
29938         split:true,
29939         initialSize: 100,
29940         minSize: 100,
29941         maxSize: 200,
29942         titlebar: true,
29943         collapsible: true
29944     },
29945     center: {
29946         titlebar: true,
29947         autoScroll:true,
29948         resizeTabs: true,
29949         minTabWidth: 50,
29950         preferredTabWidth: 150
29951     }
29952 });
29953
29954 // shorthand
29955 var CP = Roo.ContentPanel;
29956
29957 layout.beginUpdate();
29958 layout.add("north", new CP("north", "North"));
29959 layout.add("south", new CP("south", {title: "South", closable: true}));
29960 layout.add("west", new CP("west", {title: "West"}));
29961 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
29962 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
29963 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
29964 layout.getRegion("center").showPanel("center1");
29965 layout.endUpdate();
29966 </code></pre>
29967
29968 <b>The container the layout is rendered into can be either the body element or any other element.
29969 If it is not the body element, the container needs to either be an absolute positioned element,
29970 or you will need to add "position:relative" to the css of the container.  You will also need to specify
29971 the container size if it is not the body element.</b>
29972
29973 * @constructor
29974 * Create a new BorderLayout
29975 * @param {String/HTMLElement/Element} container The container this layout is bound to
29976 * @param {Object} config Configuration options
29977  */
29978 Roo.BorderLayout = function(container, config){
29979     config = config || {};
29980     Roo.BorderLayout.superclass.constructor.call(this, container, config);
29981     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
29982     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
29983         var target = this.factory.validRegions[i];
29984         if(config[target]){
29985             this.addRegion(target, config[target]);
29986         }
29987     }
29988 };
29989
29990 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
29991     /**
29992      * Creates and adds a new region if it doesn't already exist.
29993      * @param {String} target The target region key (north, south, east, west or center).
29994      * @param {Object} config The regions config object
29995      * @return {BorderLayoutRegion} The new region
29996      */
29997     addRegion : function(target, config){
29998         if(!this.regions[target]){
29999             var r = this.factory.create(target, this, config);
30000             this.bindRegion(target, r);
30001         }
30002         return this.regions[target];
30003     },
30004
30005     // private (kinda)
30006     bindRegion : function(name, r){
30007         this.regions[name] = r;
30008         r.on("visibilitychange", this.layout, this);
30009         r.on("paneladded", this.layout, this);
30010         r.on("panelremoved", this.layout, this);
30011         r.on("invalidated", this.layout, this);
30012         r.on("resized", this.onRegionResized, this);
30013         r.on("collapsed", this.onRegionCollapsed, this);
30014         r.on("expanded", this.onRegionExpanded, this);
30015     },
30016
30017     /**
30018      * Performs a layout update.
30019      */
30020     layout : function(){
30021         if(this.updating) return;
30022         var size = this.getViewSize();
30023         var w = size.width;
30024         var h = size.height;
30025         var centerW = w;
30026         var centerH = h;
30027         var centerY = 0;
30028         var centerX = 0;
30029         //var x = 0, y = 0;
30030
30031         var rs = this.regions;
30032         var north = rs["north"];
30033         var south = rs["south"]; 
30034         var west = rs["west"];
30035         var east = rs["east"];
30036         var center = rs["center"];
30037         //if(this.hideOnLayout){ // not supported anymore
30038             //c.el.setStyle("display", "none");
30039         //}
30040         if(north && north.isVisible()){
30041             var b = north.getBox();
30042             var m = north.getMargins();
30043             b.width = w - (m.left+m.right);
30044             b.x = m.left;
30045             b.y = m.top;
30046             centerY = b.height + b.y + m.bottom;
30047             centerH -= centerY;
30048             north.updateBox(this.safeBox(b));
30049         }
30050         if(south && south.isVisible()){
30051             var b = south.getBox();
30052             var m = south.getMargins();
30053             b.width = w - (m.left+m.right);
30054             b.x = m.left;
30055             var totalHeight = (b.height + m.top + m.bottom);
30056             b.y = h - totalHeight + m.top;
30057             centerH -= totalHeight;
30058             south.updateBox(this.safeBox(b));
30059         }
30060         if(west && west.isVisible()){
30061             var b = west.getBox();
30062             var m = west.getMargins();
30063             b.height = centerH - (m.top+m.bottom);
30064             b.x = m.left;
30065             b.y = centerY + m.top;
30066             var totalWidth = (b.width + m.left + m.right);
30067             centerX += totalWidth;
30068             centerW -= totalWidth;
30069             west.updateBox(this.safeBox(b));
30070         }
30071         if(east && east.isVisible()){
30072             var b = east.getBox();
30073             var m = east.getMargins();
30074             b.height = centerH - (m.top+m.bottom);
30075             var totalWidth = (b.width + m.left + m.right);
30076             b.x = w - totalWidth + m.left;
30077             b.y = centerY + m.top;
30078             centerW -= totalWidth;
30079             east.updateBox(this.safeBox(b));
30080         }
30081         if(center){
30082             var m = center.getMargins();
30083             var centerBox = {
30084                 x: centerX + m.left,
30085                 y: centerY + m.top,
30086                 width: centerW - (m.left+m.right),
30087                 height: centerH - (m.top+m.bottom)
30088             };
30089             //if(this.hideOnLayout){
30090                 //center.el.setStyle("display", "block");
30091             //}
30092             center.updateBox(this.safeBox(centerBox));
30093         }
30094         this.el.repaint();
30095         this.fireEvent("layout", this);
30096     },
30097
30098     // private
30099     safeBox : function(box){
30100         box.width = Math.max(0, box.width);
30101         box.height = Math.max(0, box.height);
30102         return box;
30103     },
30104
30105     /**
30106      * Adds a ContentPanel (or subclass) to this layout.
30107      * @param {String} target The target region key (north, south, east, west or center).
30108      * @param {Roo.ContentPanel} panel The panel to add
30109      * @return {Roo.ContentPanel} The added panel
30110      */
30111     add : function(target, panel){
30112          
30113         target = target.toLowerCase();
30114         return this.regions[target].add(panel);
30115     },
30116
30117     /**
30118      * Remove a ContentPanel (or subclass) to this layout.
30119      * @param {String} target The target region key (north, south, east, west or center).
30120      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
30121      * @return {Roo.ContentPanel} The removed panel
30122      */
30123     remove : function(target, panel){
30124         target = target.toLowerCase();
30125         return this.regions[target].remove(panel);
30126     },
30127
30128     /**
30129      * Searches all regions for a panel with the specified id
30130      * @param {String} panelId
30131      * @return {Roo.ContentPanel} The panel or null if it wasn't found
30132      */
30133     findPanel : function(panelId){
30134         var rs = this.regions;
30135         for(var target in rs){
30136             if(typeof rs[target] != "function"){
30137                 var p = rs[target].getPanel(panelId);
30138                 if(p){
30139                     return p;
30140                 }
30141             }
30142         }
30143         return null;
30144     },
30145
30146     /**
30147      * Searches all regions for a panel with the specified id and activates (shows) it.
30148      * @param {String/ContentPanel} panelId The panels id or the panel itself
30149      * @return {Roo.ContentPanel} The shown panel or null
30150      */
30151     showPanel : function(panelId) {
30152       var rs = this.regions;
30153       for(var target in rs){
30154          var r = rs[target];
30155          if(typeof r != "function"){
30156             if(r.hasPanel(panelId)){
30157                return r.showPanel(panelId);
30158             }
30159          }
30160       }
30161       return null;
30162    },
30163
30164    /**
30165      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
30166      * @param {Roo.state.Provider} provider (optional) An alternate state provider
30167      */
30168     restoreState : function(provider){
30169         if(!provider){
30170             provider = Roo.state.Manager;
30171         }
30172         var sm = new Roo.LayoutStateManager();
30173         sm.init(this, provider);
30174     },
30175
30176     /**
30177      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
30178      * object should contain properties for each region to add ContentPanels to, and each property's value should be
30179      * a valid ContentPanel config object.  Example:
30180      * <pre><code>
30181 // Create the main layout
30182 var layout = new Roo.BorderLayout('main-ct', {
30183     west: {
30184         split:true,
30185         minSize: 175,
30186         titlebar: true
30187     },
30188     center: {
30189         title:'Components'
30190     }
30191 }, 'main-ct');
30192
30193 // Create and add multiple ContentPanels at once via configs
30194 layout.batchAdd({
30195    west: {
30196        id: 'source-files',
30197        autoCreate:true,
30198        title:'Ext Source Files',
30199        autoScroll:true,
30200        fitToFrame:true
30201    },
30202    center : {
30203        el: cview,
30204        autoScroll:true,
30205        fitToFrame:true,
30206        toolbar: tb,
30207        resizeEl:'cbody'
30208    }
30209 });
30210 </code></pre>
30211      * @param {Object} regions An object containing ContentPanel configs by region name
30212      */
30213     batchAdd : function(regions){
30214         this.beginUpdate();
30215         for(var rname in regions){
30216             var lr = this.regions[rname];
30217             if(lr){
30218                 this.addTypedPanels(lr, regions[rname]);
30219             }
30220         }
30221         this.endUpdate();
30222     },
30223
30224     // private
30225     addTypedPanels : function(lr, ps){
30226         if(typeof ps == 'string'){
30227             lr.add(new Roo.ContentPanel(ps));
30228         }
30229         else if(ps instanceof Array){
30230             for(var i =0, len = ps.length; i < len; i++){
30231                 this.addTypedPanels(lr, ps[i]);
30232             }
30233         }
30234         else if(!ps.events){ // raw config?
30235             var el = ps.el;
30236             delete ps.el; // prevent conflict
30237             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
30238         }
30239         else {  // panel object assumed!
30240             lr.add(ps);
30241         }
30242     },
30243     /**
30244      * Adds a xtype elements to the layout.
30245      * <pre><code>
30246
30247 layout.addxtype({
30248        xtype : 'ContentPanel',
30249        region: 'west',
30250        items: [ .... ]
30251    }
30252 );
30253
30254 layout.addxtype({
30255         xtype : 'NestedLayoutPanel',
30256         region: 'west',
30257         layout: {
30258            center: { },
30259            west: { }   
30260         },
30261         items : [ ... list of content panels or nested layout panels.. ]
30262    }
30263 );
30264 </code></pre>
30265      * @param {Object} cfg Xtype definition of item to add.
30266      */
30267     addxtype : function(cfg)
30268     {
30269         // basically accepts a pannel...
30270         // can accept a layout region..!?!?
30271         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
30272         
30273         if (!cfg.xtype.match(/Panel$/)) {
30274             return false;
30275         }
30276         var ret = false;
30277         
30278         if (typeof(cfg.region) == 'undefined') {
30279             Roo.log("Failed to add Panel, region was not set");
30280             Roo.log(cfg);
30281             return false;
30282         }
30283         var region = cfg.region;
30284         delete cfg.region;
30285         
30286           
30287         var xitems = [];
30288         if (cfg.items) {
30289             xitems = cfg.items;
30290             delete cfg.items;
30291         }
30292         var nb = false;
30293         
30294         switch(cfg.xtype) 
30295         {
30296             case 'ContentPanel':  // ContentPanel (el, cfg)
30297             case 'ScrollPanel':  // ContentPanel (el, cfg)
30298                 if(cfg.autoCreate) {
30299                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
30300                 } else {
30301                     var el = this.el.createChild();
30302                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
30303                 }
30304                 
30305                 this.add(region, ret);
30306                 break;
30307             
30308             
30309             case 'TreePanel': // our new panel!
30310                 cfg.el = this.el.createChild();
30311                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
30312                 this.add(region, ret);
30313                 break;
30314             
30315             case 'NestedLayoutPanel': 
30316                 // create a new Layout (which is  a Border Layout...
30317                 var el = this.el.createChild();
30318                 var clayout = cfg.layout;
30319                 delete cfg.layout;
30320                 clayout.items   = clayout.items  || [];
30321                 // replace this exitems with the clayout ones..
30322                 xitems = clayout.items;
30323                  
30324                 
30325                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
30326                     cfg.background = false;
30327                 }
30328                 var layout = new Roo.BorderLayout(el, clayout);
30329                 
30330                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
30331                 //console.log('adding nested layout panel '  + cfg.toSource());
30332                 this.add(region, ret);
30333                 nb = {}; /// find first...
30334                 break;
30335                 
30336             case 'GridPanel': 
30337             
30338                 // needs grid and region
30339                 
30340                 //var el = this.getRegion(region).el.createChild();
30341                 var el = this.el.createChild();
30342                 // create the grid first...
30343                 
30344                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
30345                 delete cfg.grid;
30346                 if (region == 'center' && this.active ) {
30347                     cfg.background = false;
30348                 }
30349                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
30350                 
30351                 this.add(region, ret);
30352                 if (cfg.background) {
30353                     ret.on('activate', function(gp) {
30354                         if (!gp.grid.rendered) {
30355                             gp.grid.render();
30356                         }
30357                     });
30358                 } else {
30359                     grid.render();
30360                 }
30361                 break;
30362            
30363                
30364                 
30365                 
30366             default: 
30367                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
30368                 return null;
30369              // GridPanel (grid, cfg)
30370             
30371         }
30372         this.beginUpdate();
30373         // add children..
30374         var region = '';
30375         var abn = {};
30376         Roo.each(xitems, function(i)  {
30377             region = nb && i.region ? i.region : false;
30378             
30379             var add = ret.addxtype(i);
30380            
30381             if (region) {
30382                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
30383                 if (!i.background) {
30384                     abn[region] = nb[region] ;
30385                 }
30386             }
30387             
30388         });
30389         this.endUpdate();
30390
30391         // make the last non-background panel active..
30392         //if (nb) { Roo.log(abn); }
30393         if (nb) {
30394             
30395             for(var r in abn) {
30396                 region = this.getRegion(r);
30397                 if (region) {
30398                     // tried using nb[r], but it does not work..
30399                      
30400                     region.showPanel(abn[r]);
30401                    
30402                 }
30403             }
30404         }
30405         return ret;
30406         
30407     }
30408 });
30409
30410 /**
30411  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
30412  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
30413  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
30414  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
30415  * <pre><code>
30416 // shorthand
30417 var CP = Roo.ContentPanel;
30418
30419 var layout = Roo.BorderLayout.create({
30420     north: {
30421         initialSize: 25,
30422         titlebar: false,
30423         panels: [new CP("north", "North")]
30424     },
30425     west: {
30426         split:true,
30427         initialSize: 200,
30428         minSize: 175,
30429         maxSize: 400,
30430         titlebar: true,
30431         collapsible: true,
30432         panels: [new CP("west", {title: "West"})]
30433     },
30434     east: {
30435         split:true,
30436         initialSize: 202,
30437         minSize: 175,
30438         maxSize: 400,
30439         titlebar: true,
30440         collapsible: true,
30441         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
30442     },
30443     south: {
30444         split:true,
30445         initialSize: 100,
30446         minSize: 100,
30447         maxSize: 200,
30448         titlebar: true,
30449         collapsible: true,
30450         panels: [new CP("south", {title: "South", closable: true})]
30451     },
30452     center: {
30453         titlebar: true,
30454         autoScroll:true,
30455         resizeTabs: true,
30456         minTabWidth: 50,
30457         preferredTabWidth: 150,
30458         panels: [
30459             new CP("center1", {title: "Close Me", closable: true}),
30460             new CP("center2", {title: "Center Panel", closable: false})
30461         ]
30462     }
30463 }, document.body);
30464
30465 layout.getRegion("center").showPanel("center1");
30466 </code></pre>
30467  * @param config
30468  * @param targetEl
30469  */
30470 Roo.BorderLayout.create = function(config, targetEl){
30471     var layout = new Roo.BorderLayout(targetEl || document.body, config);
30472     layout.beginUpdate();
30473     var regions = Roo.BorderLayout.RegionFactory.validRegions;
30474     for(var j = 0, jlen = regions.length; j < jlen; j++){
30475         var lr = regions[j];
30476         if(layout.regions[lr] && config[lr].panels){
30477             var r = layout.regions[lr];
30478             var ps = config[lr].panels;
30479             layout.addTypedPanels(r, ps);
30480         }
30481     }
30482     layout.endUpdate();
30483     return layout;
30484 };
30485
30486 // private
30487 Roo.BorderLayout.RegionFactory = {
30488     // private
30489     validRegions : ["north","south","east","west","center"],
30490
30491     // private
30492     create : function(target, mgr, config){
30493         target = target.toLowerCase();
30494         if(config.lightweight || config.basic){
30495             return new Roo.BasicLayoutRegion(mgr, config, target);
30496         }
30497         switch(target){
30498             case "north":
30499                 return new Roo.NorthLayoutRegion(mgr, config);
30500             case "south":
30501                 return new Roo.SouthLayoutRegion(mgr, config);
30502             case "east":
30503                 return new Roo.EastLayoutRegion(mgr, config);
30504             case "west":
30505                 return new Roo.WestLayoutRegion(mgr, config);
30506             case "center":
30507                 return new Roo.CenterLayoutRegion(mgr, config);
30508         }
30509         throw 'Layout region "'+target+'" not supported.';
30510     }
30511 };/*
30512  * Based on:
30513  * Ext JS Library 1.1.1
30514  * Copyright(c) 2006-2007, Ext JS, LLC.
30515  *
30516  * Originally Released Under LGPL - original licence link has changed is not relivant.
30517  *
30518  * Fork - LGPL
30519  * <script type="text/javascript">
30520  */
30521  
30522 /**
30523  * @class Roo.BasicLayoutRegion
30524  * @extends Roo.util.Observable
30525  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
30526  * and does not have a titlebar, tabs or any other features. All it does is size and position 
30527  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
30528  */
30529 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
30530     this.mgr = mgr;
30531     this.position  = pos;
30532     this.events = {
30533         /**
30534          * @scope Roo.BasicLayoutRegion
30535          */
30536         
30537         /**
30538          * @event beforeremove
30539          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
30540          * @param {Roo.LayoutRegion} this
30541          * @param {Roo.ContentPanel} panel The panel
30542          * @param {Object} e The cancel event object
30543          */
30544         "beforeremove" : true,
30545         /**
30546          * @event invalidated
30547          * Fires when the layout for this region is changed.
30548          * @param {Roo.LayoutRegion} this
30549          */
30550         "invalidated" : true,
30551         /**
30552          * @event visibilitychange
30553          * Fires when this region is shown or hidden 
30554          * @param {Roo.LayoutRegion} this
30555          * @param {Boolean} visibility true or false
30556          */
30557         "visibilitychange" : true,
30558         /**
30559          * @event paneladded
30560          * Fires when a panel is added. 
30561          * @param {Roo.LayoutRegion} this
30562          * @param {Roo.ContentPanel} panel The panel
30563          */
30564         "paneladded" : true,
30565         /**
30566          * @event panelremoved
30567          * Fires when a panel is removed. 
30568          * @param {Roo.LayoutRegion} this
30569          * @param {Roo.ContentPanel} panel The panel
30570          */
30571         "panelremoved" : true,
30572         /**
30573          * @event collapsed
30574          * Fires when this region is collapsed.
30575          * @param {Roo.LayoutRegion} this
30576          */
30577         "collapsed" : true,
30578         /**
30579          * @event expanded
30580          * Fires when this region is expanded.
30581          * @param {Roo.LayoutRegion} this
30582          */
30583         "expanded" : true,
30584         /**
30585          * @event slideshow
30586          * Fires when this region is slid into view.
30587          * @param {Roo.LayoutRegion} this
30588          */
30589         "slideshow" : true,
30590         /**
30591          * @event slidehide
30592          * Fires when this region slides out of view. 
30593          * @param {Roo.LayoutRegion} this
30594          */
30595         "slidehide" : true,
30596         /**
30597          * @event panelactivated
30598          * Fires when a panel is activated. 
30599          * @param {Roo.LayoutRegion} this
30600          * @param {Roo.ContentPanel} panel The activated panel
30601          */
30602         "panelactivated" : true,
30603         /**
30604          * @event resized
30605          * Fires when the user resizes this region. 
30606          * @param {Roo.LayoutRegion} this
30607          * @param {Number} newSize The new size (width for east/west, height for north/south)
30608          */
30609         "resized" : true
30610     };
30611     /** A collection of panels in this region. @type Roo.util.MixedCollection */
30612     this.panels = new Roo.util.MixedCollection();
30613     this.panels.getKey = this.getPanelId.createDelegate(this);
30614     this.box = null;
30615     this.activePanel = null;
30616     // ensure listeners are added...
30617     
30618     if (config.listeners || config.events) {
30619         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
30620             listeners : config.listeners || {},
30621             events : config.events || {}
30622         });
30623     }
30624     
30625     if(skipConfig !== true){
30626         this.applyConfig(config);
30627     }
30628 };
30629
30630 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
30631     getPanelId : function(p){
30632         return p.getId();
30633     },
30634     
30635     applyConfig : function(config){
30636         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
30637         this.config = config;
30638         
30639     },
30640     
30641     /**
30642      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
30643      * the width, for horizontal (north, south) the height.
30644      * @param {Number} newSize The new width or height
30645      */
30646     resizeTo : function(newSize){
30647         var el = this.el ? this.el :
30648                  (this.activePanel ? this.activePanel.getEl() : null);
30649         if(el){
30650             switch(this.position){
30651                 case "east":
30652                 case "west":
30653                     el.setWidth(newSize);
30654                     this.fireEvent("resized", this, newSize);
30655                 break;
30656                 case "north":
30657                 case "south":
30658                     el.setHeight(newSize);
30659                     this.fireEvent("resized", this, newSize);
30660                 break;                
30661             }
30662         }
30663     },
30664     
30665     getBox : function(){
30666         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
30667     },
30668     
30669     getMargins : function(){
30670         return this.margins;
30671     },
30672     
30673     updateBox : function(box){
30674         this.box = box;
30675         var el = this.activePanel.getEl();
30676         el.dom.style.left = box.x + "px";
30677         el.dom.style.top = box.y + "px";
30678         this.activePanel.setSize(box.width, box.height);
30679     },
30680     
30681     /**
30682      * Returns the container element for this region.
30683      * @return {Roo.Element}
30684      */
30685     getEl : function(){
30686         return this.activePanel;
30687     },
30688     
30689     /**
30690      * Returns true if this region is currently visible.
30691      * @return {Boolean}
30692      */
30693     isVisible : function(){
30694         return this.activePanel ? true : false;
30695     },
30696     
30697     setActivePanel : function(panel){
30698         panel = this.getPanel(panel);
30699         if(this.activePanel && this.activePanel != panel){
30700             this.activePanel.setActiveState(false);
30701             this.activePanel.getEl().setLeftTop(-10000,-10000);
30702         }
30703         this.activePanel = panel;
30704         panel.setActiveState(true);
30705         if(this.box){
30706             panel.setSize(this.box.width, this.box.height);
30707         }
30708         this.fireEvent("panelactivated", this, panel);
30709         this.fireEvent("invalidated");
30710     },
30711     
30712     /**
30713      * Show the specified panel.
30714      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
30715      * @return {Roo.ContentPanel} The shown panel or null
30716      */
30717     showPanel : function(panel){
30718         if(panel = this.getPanel(panel)){
30719             this.setActivePanel(panel);
30720         }
30721         return panel;
30722     },
30723     
30724     /**
30725      * Get the active panel for this region.
30726      * @return {Roo.ContentPanel} The active panel or null
30727      */
30728     getActivePanel : function(){
30729         return this.activePanel;
30730     },
30731     
30732     /**
30733      * Add the passed ContentPanel(s)
30734      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30735      * @return {Roo.ContentPanel} The panel added (if only one was added)
30736      */
30737     add : function(panel){
30738         if(arguments.length > 1){
30739             for(var i = 0, len = arguments.length; i < len; i++) {
30740                 this.add(arguments[i]);
30741             }
30742             return null;
30743         }
30744         if(this.hasPanel(panel)){
30745             this.showPanel(panel);
30746             return panel;
30747         }
30748         var el = panel.getEl();
30749         if(el.dom.parentNode != this.mgr.el.dom){
30750             this.mgr.el.dom.appendChild(el.dom);
30751         }
30752         if(panel.setRegion){
30753             panel.setRegion(this);
30754         }
30755         this.panels.add(panel);
30756         el.setStyle("position", "absolute");
30757         if(!panel.background){
30758             this.setActivePanel(panel);
30759             if(this.config.initialSize && this.panels.getCount()==1){
30760                 this.resizeTo(this.config.initialSize);
30761             }
30762         }
30763         this.fireEvent("paneladded", this, panel);
30764         return panel;
30765     },
30766     
30767     /**
30768      * Returns true if the panel is in this region.
30769      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
30770      * @return {Boolean}
30771      */
30772     hasPanel : function(panel){
30773         if(typeof panel == "object"){ // must be panel obj
30774             panel = panel.getId();
30775         }
30776         return this.getPanel(panel) ? true : false;
30777     },
30778     
30779     /**
30780      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30781      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
30782      * @param {Boolean} preservePanel Overrides the config preservePanel option
30783      * @return {Roo.ContentPanel} The panel that was removed
30784      */
30785     remove : function(panel, preservePanel){
30786         panel = this.getPanel(panel);
30787         if(!panel){
30788             return null;
30789         }
30790         var e = {};
30791         this.fireEvent("beforeremove", this, panel, e);
30792         if(e.cancel === true){
30793             return null;
30794         }
30795         var panelId = panel.getId();
30796         this.panels.removeKey(panelId);
30797         return panel;
30798     },
30799     
30800     /**
30801      * Returns the panel specified or null if it's not in this region.
30802      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
30803      * @return {Roo.ContentPanel}
30804      */
30805     getPanel : function(id){
30806         if(typeof id == "object"){ // must be panel obj
30807             return id;
30808         }
30809         return this.panels.get(id);
30810     },
30811     
30812     /**
30813      * Returns this regions position (north/south/east/west/center).
30814      * @return {String} 
30815      */
30816     getPosition: function(){
30817         return this.position;    
30818     }
30819 });/*
30820  * Based on:
30821  * Ext JS Library 1.1.1
30822  * Copyright(c) 2006-2007, Ext JS, LLC.
30823  *
30824  * Originally Released Under LGPL - original licence link has changed is not relivant.
30825  *
30826  * Fork - LGPL
30827  * <script type="text/javascript">
30828  */
30829  
30830 /**
30831  * @class Roo.LayoutRegion
30832  * @extends Roo.BasicLayoutRegion
30833  * This class represents a region in a layout manager.
30834  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
30835  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
30836  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
30837  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
30838  * @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})
30839  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
30840  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
30841  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
30842  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
30843  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
30844  * @cfg {String}    title           The title for the region (overrides panel titles)
30845  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
30846  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
30847  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
30848  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
30849  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
30850  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
30851  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
30852  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
30853  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
30854  * @cfg {Boolean}   showPin         True to show a pin button
30855  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
30856  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
30857  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
30858  * @cfg {Number}    width           For East/West panels
30859  * @cfg {Number}    height          For North/South panels
30860  * @cfg {Boolean}   split           To show the splitter
30861  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
30862  */
30863 Roo.LayoutRegion = function(mgr, config, pos){
30864     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
30865     var dh = Roo.DomHelper;
30866     /** This region's container element 
30867     * @type Roo.Element */
30868     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
30869     /** This region's title element 
30870     * @type Roo.Element */
30871
30872     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
30873         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
30874         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
30875     ]}, true);
30876     this.titleEl.enableDisplayMode();
30877     /** This region's title text element 
30878     * @type HTMLElement */
30879     this.titleTextEl = this.titleEl.dom.firstChild;
30880     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
30881     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
30882     this.closeBtn.enableDisplayMode();
30883     this.closeBtn.on("click", this.closeClicked, this);
30884     this.closeBtn.hide();
30885
30886     this.createBody(config);
30887     this.visible = true;
30888     this.collapsed = false;
30889
30890     if(config.hideWhenEmpty){
30891         this.hide();
30892         this.on("paneladded", this.validateVisibility, this);
30893         this.on("panelremoved", this.validateVisibility, this);
30894     }
30895     this.applyConfig(config);
30896 };
30897
30898 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
30899
30900     createBody : function(){
30901         /** This region's body element 
30902         * @type Roo.Element */
30903         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
30904     },
30905
30906     applyConfig : function(c){
30907         if(c.collapsible && this.position != "center" && !this.collapsedEl){
30908             var dh = Roo.DomHelper;
30909             if(c.titlebar !== false){
30910                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
30911                 this.collapseBtn.on("click", this.collapse, this);
30912                 this.collapseBtn.enableDisplayMode();
30913
30914                 if(c.showPin === true || this.showPin){
30915                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
30916                     this.stickBtn.enableDisplayMode();
30917                     this.stickBtn.on("click", this.expand, this);
30918                     this.stickBtn.hide();
30919                 }
30920             }
30921             /** This region's collapsed element
30922             * @type Roo.Element */
30923             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
30924                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
30925             ]}, true);
30926             if(c.floatable !== false){
30927                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
30928                this.collapsedEl.on("click", this.collapseClick, this);
30929             }
30930
30931             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
30932                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
30933                    id: "message", unselectable: "on", style:{"float":"left"}});
30934                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
30935              }
30936             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
30937             this.expandBtn.on("click", this.expand, this);
30938         }
30939         if(this.collapseBtn){
30940             this.collapseBtn.setVisible(c.collapsible == true);
30941         }
30942         this.cmargins = c.cmargins || this.cmargins ||
30943                          (this.position == "west" || this.position == "east" ?
30944                              {top: 0, left: 2, right:2, bottom: 0} :
30945                              {top: 2, left: 0, right:0, bottom: 2});
30946         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
30947         this.bottomTabs = c.tabPosition != "top";
30948         this.autoScroll = c.autoScroll || false;
30949         if(this.autoScroll){
30950             this.bodyEl.setStyle("overflow", "auto");
30951         }else{
30952             this.bodyEl.setStyle("overflow", "hidden");
30953         }
30954         //if(c.titlebar !== false){
30955             if((!c.titlebar && !c.title) || c.titlebar === false){
30956                 this.titleEl.hide();
30957             }else{
30958                 this.titleEl.show();
30959                 if(c.title){
30960                     this.titleTextEl.innerHTML = c.title;
30961                 }
30962             }
30963         //}
30964         this.duration = c.duration || .30;
30965         this.slideDuration = c.slideDuration || .45;
30966         this.config = c;
30967         if(c.collapsed){
30968             this.collapse(true);
30969         }
30970         if(c.hidden){
30971             this.hide();
30972         }
30973     },
30974     /**
30975      * Returns true if this region is currently visible.
30976      * @return {Boolean}
30977      */
30978     isVisible : function(){
30979         return this.visible;
30980     },
30981
30982     /**
30983      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
30984      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
30985      */
30986     setCollapsedTitle : function(title){
30987         title = title || "&#160;";
30988         if(this.collapsedTitleTextEl){
30989             this.collapsedTitleTextEl.innerHTML = title;
30990         }
30991     },
30992
30993     getBox : function(){
30994         var b;
30995         if(!this.collapsed){
30996             b = this.el.getBox(false, true);
30997         }else{
30998             b = this.collapsedEl.getBox(false, true);
30999         }
31000         return b;
31001     },
31002
31003     getMargins : function(){
31004         return this.collapsed ? this.cmargins : this.margins;
31005     },
31006
31007     highlight : function(){
31008         this.el.addClass("x-layout-panel-dragover");
31009     },
31010
31011     unhighlight : function(){
31012         this.el.removeClass("x-layout-panel-dragover");
31013     },
31014
31015     updateBox : function(box){
31016         this.box = box;
31017         if(!this.collapsed){
31018             this.el.dom.style.left = box.x + "px";
31019             this.el.dom.style.top = box.y + "px";
31020             this.updateBody(box.width, box.height);
31021         }else{
31022             this.collapsedEl.dom.style.left = box.x + "px";
31023             this.collapsedEl.dom.style.top = box.y + "px";
31024             this.collapsedEl.setSize(box.width, box.height);
31025         }
31026         if(this.tabs){
31027             this.tabs.autoSizeTabs();
31028         }
31029     },
31030
31031     updateBody : function(w, h){
31032         if(w !== null){
31033             this.el.setWidth(w);
31034             w -= this.el.getBorderWidth("rl");
31035             if(this.config.adjustments){
31036                 w += this.config.adjustments[0];
31037             }
31038         }
31039         if(h !== null){
31040             this.el.setHeight(h);
31041             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
31042             h -= this.el.getBorderWidth("tb");
31043             if(this.config.adjustments){
31044                 h += this.config.adjustments[1];
31045             }
31046             this.bodyEl.setHeight(h);
31047             if(this.tabs){
31048                 h = this.tabs.syncHeight(h);
31049             }
31050         }
31051         if(this.panelSize){
31052             w = w !== null ? w : this.panelSize.width;
31053             h = h !== null ? h : this.panelSize.height;
31054         }
31055         if(this.activePanel){
31056             var el = this.activePanel.getEl();
31057             w = w !== null ? w : el.getWidth();
31058             h = h !== null ? h : el.getHeight();
31059             this.panelSize = {width: w, height: h};
31060             this.activePanel.setSize(w, h);
31061         }
31062         if(Roo.isIE && this.tabs){
31063             this.tabs.el.repaint();
31064         }
31065     },
31066
31067     /**
31068      * Returns the container element for this region.
31069      * @return {Roo.Element}
31070      */
31071     getEl : function(){
31072         return this.el;
31073     },
31074
31075     /**
31076      * Hides this region.
31077      */
31078     hide : function(){
31079         if(!this.collapsed){
31080             this.el.dom.style.left = "-2000px";
31081             this.el.hide();
31082         }else{
31083             this.collapsedEl.dom.style.left = "-2000px";
31084             this.collapsedEl.hide();
31085         }
31086         this.visible = false;
31087         this.fireEvent("visibilitychange", this, false);
31088     },
31089
31090     /**
31091      * Shows this region if it was previously hidden.
31092      */
31093     show : function(){
31094         if(!this.collapsed){
31095             this.el.show();
31096         }else{
31097             this.collapsedEl.show();
31098         }
31099         this.visible = true;
31100         this.fireEvent("visibilitychange", this, true);
31101     },
31102
31103     closeClicked : function(){
31104         if(this.activePanel){
31105             this.remove(this.activePanel);
31106         }
31107     },
31108
31109     collapseClick : function(e){
31110         if(this.isSlid){
31111            e.stopPropagation();
31112            this.slideIn();
31113         }else{
31114            e.stopPropagation();
31115            this.slideOut();
31116         }
31117     },
31118
31119     /**
31120      * Collapses this region.
31121      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
31122      */
31123     collapse : function(skipAnim){
31124         if(this.collapsed) return;
31125         this.collapsed = true;
31126         if(this.split){
31127             this.split.el.hide();
31128         }
31129         if(this.config.animate && skipAnim !== true){
31130             this.fireEvent("invalidated", this);
31131             this.animateCollapse();
31132         }else{
31133             this.el.setLocation(-20000,-20000);
31134             this.el.hide();
31135             this.collapsedEl.show();
31136             this.fireEvent("collapsed", this);
31137             this.fireEvent("invalidated", this);
31138         }
31139     },
31140
31141     animateCollapse : function(){
31142         // overridden
31143     },
31144
31145     /**
31146      * Expands this region if it was previously collapsed.
31147      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
31148      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
31149      */
31150     expand : function(e, skipAnim){
31151         if(e) e.stopPropagation();
31152         if(!this.collapsed || this.el.hasActiveFx()) return;
31153         if(this.isSlid){
31154             this.afterSlideIn();
31155             skipAnim = true;
31156         }
31157         this.collapsed = false;
31158         if(this.config.animate && skipAnim !== true){
31159             this.animateExpand();
31160         }else{
31161             this.el.show();
31162             if(this.split){
31163                 this.split.el.show();
31164             }
31165             this.collapsedEl.setLocation(-2000,-2000);
31166             this.collapsedEl.hide();
31167             this.fireEvent("invalidated", this);
31168             this.fireEvent("expanded", this);
31169         }
31170     },
31171
31172     animateExpand : function(){
31173         // overridden
31174     },
31175
31176     initTabs : function()
31177     {
31178         this.bodyEl.setStyle("overflow", "hidden");
31179         var ts = new Roo.TabPanel(
31180                 this.bodyEl.dom,
31181                 {
31182                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
31183                     disableTooltips: this.config.disableTabTips,
31184                     toolbar : this.config.toolbar
31185                 }
31186         );
31187         if(this.config.hideTabs){
31188             ts.stripWrap.setDisplayed(false);
31189         }
31190         this.tabs = ts;
31191         ts.resizeTabs = this.config.resizeTabs === true;
31192         ts.minTabWidth = this.config.minTabWidth || 40;
31193         ts.maxTabWidth = this.config.maxTabWidth || 250;
31194         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
31195         ts.monitorResize = false;
31196         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
31197         ts.bodyEl.addClass('x-layout-tabs-body');
31198         this.panels.each(this.initPanelAsTab, this);
31199     },
31200
31201     initPanelAsTab : function(panel){
31202         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
31203                     this.config.closeOnTab && panel.isClosable());
31204         if(panel.tabTip !== undefined){
31205             ti.setTooltip(panel.tabTip);
31206         }
31207         ti.on("activate", function(){
31208               this.setActivePanel(panel);
31209         }, this);
31210         if(this.config.closeOnTab){
31211             ti.on("beforeclose", function(t, e){
31212                 e.cancel = true;
31213                 this.remove(panel);
31214             }, this);
31215         }
31216         return ti;
31217     },
31218
31219     updatePanelTitle : function(panel, title){
31220         if(this.activePanel == panel){
31221             this.updateTitle(title);
31222         }
31223         if(this.tabs){
31224             var ti = this.tabs.getTab(panel.getEl().id);
31225             ti.setText(title);
31226             if(panel.tabTip !== undefined){
31227                 ti.setTooltip(panel.tabTip);
31228             }
31229         }
31230     },
31231
31232     updateTitle : function(title){
31233         if(this.titleTextEl && !this.config.title){
31234             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
31235         }
31236     },
31237
31238     setActivePanel : function(panel){
31239         panel = this.getPanel(panel);
31240         if(this.activePanel && this.activePanel != panel){
31241             this.activePanel.setActiveState(false);
31242         }
31243         this.activePanel = panel;
31244         panel.setActiveState(true);
31245         if(this.panelSize){
31246             panel.setSize(this.panelSize.width, this.panelSize.height);
31247         }
31248         if(this.closeBtn){
31249             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
31250         }
31251         this.updateTitle(panel.getTitle());
31252         if(this.tabs){
31253             this.fireEvent("invalidated", this);
31254         }
31255         this.fireEvent("panelactivated", this, panel);
31256     },
31257
31258     /**
31259      * Shows the specified panel.
31260      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
31261      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
31262      */
31263     showPanel : function(panel){
31264         if(panel = this.getPanel(panel)){
31265             if(this.tabs){
31266                 var tab = this.tabs.getTab(panel.getEl().id);
31267                 if(tab.isHidden()){
31268                     this.tabs.unhideTab(tab.id);
31269                 }
31270                 tab.activate();
31271             }else{
31272                 this.setActivePanel(panel);
31273             }
31274         }
31275         return panel;
31276     },
31277
31278     /**
31279      * Get the active panel for this region.
31280      * @return {Roo.ContentPanel} The active panel or null
31281      */
31282     getActivePanel : function(){
31283         return this.activePanel;
31284     },
31285
31286     validateVisibility : function(){
31287         if(this.panels.getCount() < 1){
31288             this.updateTitle("&#160;");
31289             this.closeBtn.hide();
31290             this.hide();
31291         }else{
31292             if(!this.isVisible()){
31293                 this.show();
31294             }
31295         }
31296     },
31297
31298     /**
31299      * Adds the passed ContentPanel(s) to this region.
31300      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
31301      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
31302      */
31303     add : function(panel){
31304         if(arguments.length > 1){
31305             for(var i = 0, len = arguments.length; i < len; i++) {
31306                 this.add(arguments[i]);
31307             }
31308             return null;
31309         }
31310         if(this.hasPanel(panel)){
31311             this.showPanel(panel);
31312             return panel;
31313         }
31314         panel.setRegion(this);
31315         this.panels.add(panel);
31316         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
31317             this.bodyEl.dom.appendChild(panel.getEl().dom);
31318             if(panel.background !== true){
31319                 this.setActivePanel(panel);
31320             }
31321             this.fireEvent("paneladded", this, panel);
31322             return panel;
31323         }
31324         if(!this.tabs){
31325             this.initTabs();
31326         }else{
31327             this.initPanelAsTab(panel);
31328         }
31329         if(panel.background !== true){
31330             this.tabs.activate(panel.getEl().id);
31331         }
31332         this.fireEvent("paneladded", this, panel);
31333         return panel;
31334     },
31335
31336     /**
31337      * Hides the tab for the specified panel.
31338      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
31339      */
31340     hidePanel : function(panel){
31341         if(this.tabs && (panel = this.getPanel(panel))){
31342             this.tabs.hideTab(panel.getEl().id);
31343         }
31344     },
31345
31346     /**
31347      * Unhides the tab for a previously hidden panel.
31348      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
31349      */
31350     unhidePanel : function(panel){
31351         if(this.tabs && (panel = this.getPanel(panel))){
31352             this.tabs.unhideTab(panel.getEl().id);
31353         }
31354     },
31355
31356     clearPanels : function(){
31357         while(this.panels.getCount() > 0){
31358              this.remove(this.panels.first());
31359         }
31360     },
31361
31362     /**
31363      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
31364      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
31365      * @param {Boolean} preservePanel Overrides the config preservePanel option
31366      * @return {Roo.ContentPanel} The panel that was removed
31367      */
31368     remove : function(panel, preservePanel){
31369         panel = this.getPanel(panel);
31370         if(!panel){
31371             return null;
31372         }
31373         var e = {};
31374         this.fireEvent("beforeremove", this, panel, e);
31375         if(e.cancel === true){
31376             return null;
31377         }
31378         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
31379         var panelId = panel.getId();
31380         this.panels.removeKey(panelId);
31381         if(preservePanel){
31382             document.body.appendChild(panel.getEl().dom);
31383         }
31384         if(this.tabs){
31385             this.tabs.removeTab(panel.getEl().id);
31386         }else if (!preservePanel){
31387             this.bodyEl.dom.removeChild(panel.getEl().dom);
31388         }
31389         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
31390             var p = this.panels.first();
31391             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
31392             tempEl.appendChild(p.getEl().dom);
31393             this.bodyEl.update("");
31394             this.bodyEl.dom.appendChild(p.getEl().dom);
31395             tempEl = null;
31396             this.updateTitle(p.getTitle());
31397             this.tabs = null;
31398             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
31399             this.setActivePanel(p);
31400         }
31401         panel.setRegion(null);
31402         if(this.activePanel == panel){
31403             this.activePanel = null;
31404         }
31405         if(this.config.autoDestroy !== false && preservePanel !== true){
31406             try{panel.destroy();}catch(e){}
31407         }
31408         this.fireEvent("panelremoved", this, panel);
31409         return panel;
31410     },
31411
31412     /**
31413      * Returns the TabPanel component used by this region
31414      * @return {Roo.TabPanel}
31415      */
31416     getTabs : function(){
31417         return this.tabs;
31418     },
31419
31420     createTool : function(parentEl, className){
31421         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
31422             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
31423         btn.addClassOnOver("x-layout-tools-button-over");
31424         return btn;
31425     }
31426 });/*
31427  * Based on:
31428  * Ext JS Library 1.1.1
31429  * Copyright(c) 2006-2007, Ext JS, LLC.
31430  *
31431  * Originally Released Under LGPL - original licence link has changed is not relivant.
31432  *
31433  * Fork - LGPL
31434  * <script type="text/javascript">
31435  */
31436  
31437
31438
31439 /**
31440  * @class Roo.SplitLayoutRegion
31441  * @extends Roo.LayoutRegion
31442  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
31443  */
31444 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
31445     this.cursor = cursor;
31446     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
31447 };
31448
31449 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
31450     splitTip : "Drag to resize.",
31451     collapsibleSplitTip : "Drag to resize. Double click to hide.",
31452     useSplitTips : false,
31453
31454     applyConfig : function(config){
31455         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
31456         if(config.split){
31457             if(!this.split){
31458                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
31459                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
31460                 /** The SplitBar for this region 
31461                 * @type Roo.SplitBar */
31462                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
31463                 this.split.on("moved", this.onSplitMove, this);
31464                 this.split.useShim = config.useShim === true;
31465                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
31466                 if(this.useSplitTips){
31467                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
31468                 }
31469                 if(config.collapsible){
31470                     this.split.el.on("dblclick", this.collapse,  this);
31471                 }
31472             }
31473             if(typeof config.minSize != "undefined"){
31474                 this.split.minSize = config.minSize;
31475             }
31476             if(typeof config.maxSize != "undefined"){
31477                 this.split.maxSize = config.maxSize;
31478             }
31479             if(config.hideWhenEmpty || config.hidden || config.collapsed){
31480                 this.hideSplitter();
31481             }
31482         }
31483     },
31484
31485     getHMaxSize : function(){
31486          var cmax = this.config.maxSize || 10000;
31487          var center = this.mgr.getRegion("center");
31488          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
31489     },
31490
31491     getVMaxSize : function(){
31492          var cmax = this.config.maxSize || 10000;
31493          var center = this.mgr.getRegion("center");
31494          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
31495     },
31496
31497     onSplitMove : function(split, newSize){
31498         this.fireEvent("resized", this, newSize);
31499     },
31500     
31501     /** 
31502      * Returns the {@link Roo.SplitBar} for this region.
31503      * @return {Roo.SplitBar}
31504      */
31505     getSplitBar : function(){
31506         return this.split;
31507     },
31508     
31509     hide : function(){
31510         this.hideSplitter();
31511         Roo.SplitLayoutRegion.superclass.hide.call(this);
31512     },
31513
31514     hideSplitter : function(){
31515         if(this.split){
31516             this.split.el.setLocation(-2000,-2000);
31517             this.split.el.hide();
31518         }
31519     },
31520
31521     show : function(){
31522         if(this.split){
31523             this.split.el.show();
31524         }
31525         Roo.SplitLayoutRegion.superclass.show.call(this);
31526     },
31527     
31528     beforeSlide: function(){
31529         if(Roo.isGecko){// firefox overflow auto bug workaround
31530             this.bodyEl.clip();
31531             if(this.tabs) this.tabs.bodyEl.clip();
31532             if(this.activePanel){
31533                 this.activePanel.getEl().clip();
31534                 
31535                 if(this.activePanel.beforeSlide){
31536                     this.activePanel.beforeSlide();
31537                 }
31538             }
31539         }
31540     },
31541     
31542     afterSlide : function(){
31543         if(Roo.isGecko){// firefox overflow auto bug workaround
31544             this.bodyEl.unclip();
31545             if(this.tabs) this.tabs.bodyEl.unclip();
31546             if(this.activePanel){
31547                 this.activePanel.getEl().unclip();
31548                 if(this.activePanel.afterSlide){
31549                     this.activePanel.afterSlide();
31550                 }
31551             }
31552         }
31553     },
31554
31555     initAutoHide : function(){
31556         if(this.autoHide !== false){
31557             if(!this.autoHideHd){
31558                 var st = new Roo.util.DelayedTask(this.slideIn, this);
31559                 this.autoHideHd = {
31560                     "mouseout": function(e){
31561                         if(!e.within(this.el, true)){
31562                             st.delay(500);
31563                         }
31564                     },
31565                     "mouseover" : function(e){
31566                         st.cancel();
31567                     },
31568                     scope : this
31569                 };
31570             }
31571             this.el.on(this.autoHideHd);
31572         }
31573     },
31574
31575     clearAutoHide : function(){
31576         if(this.autoHide !== false){
31577             this.el.un("mouseout", this.autoHideHd.mouseout);
31578             this.el.un("mouseover", this.autoHideHd.mouseover);
31579         }
31580     },
31581
31582     clearMonitor : function(){
31583         Roo.get(document).un("click", this.slideInIf, this);
31584     },
31585
31586     // these names are backwards but not changed for compat
31587     slideOut : function(){
31588         if(this.isSlid || this.el.hasActiveFx()){
31589             return;
31590         }
31591         this.isSlid = true;
31592         if(this.collapseBtn){
31593             this.collapseBtn.hide();
31594         }
31595         this.closeBtnState = this.closeBtn.getStyle('display');
31596         this.closeBtn.hide();
31597         if(this.stickBtn){
31598             this.stickBtn.show();
31599         }
31600         this.el.show();
31601         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
31602         this.beforeSlide();
31603         this.el.setStyle("z-index", 10001);
31604         this.el.slideIn(this.getSlideAnchor(), {
31605             callback: function(){
31606                 this.afterSlide();
31607                 this.initAutoHide();
31608                 Roo.get(document).on("click", this.slideInIf, this);
31609                 this.fireEvent("slideshow", this);
31610             },
31611             scope: this,
31612             block: true
31613         });
31614     },
31615
31616     afterSlideIn : function(){
31617         this.clearAutoHide();
31618         this.isSlid = false;
31619         this.clearMonitor();
31620         this.el.setStyle("z-index", "");
31621         if(this.collapseBtn){
31622             this.collapseBtn.show();
31623         }
31624         this.closeBtn.setStyle('display', this.closeBtnState);
31625         if(this.stickBtn){
31626             this.stickBtn.hide();
31627         }
31628         this.fireEvent("slidehide", this);
31629     },
31630
31631     slideIn : function(cb){
31632         if(!this.isSlid || this.el.hasActiveFx()){
31633             Roo.callback(cb);
31634             return;
31635         }
31636         this.isSlid = false;
31637         this.beforeSlide();
31638         this.el.slideOut(this.getSlideAnchor(), {
31639             callback: function(){
31640                 this.el.setLeftTop(-10000, -10000);
31641                 this.afterSlide();
31642                 this.afterSlideIn();
31643                 Roo.callback(cb);
31644             },
31645             scope: this,
31646             block: true
31647         });
31648     },
31649     
31650     slideInIf : function(e){
31651         if(!e.within(this.el)){
31652             this.slideIn();
31653         }
31654     },
31655
31656     animateCollapse : function(){
31657         this.beforeSlide();
31658         this.el.setStyle("z-index", 20000);
31659         var anchor = this.getSlideAnchor();
31660         this.el.slideOut(anchor, {
31661             callback : function(){
31662                 this.el.setStyle("z-index", "");
31663                 this.collapsedEl.slideIn(anchor, {duration:.3});
31664                 this.afterSlide();
31665                 this.el.setLocation(-10000,-10000);
31666                 this.el.hide();
31667                 this.fireEvent("collapsed", this);
31668             },
31669             scope: this,
31670             block: true
31671         });
31672     },
31673
31674     animateExpand : function(){
31675         this.beforeSlide();
31676         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
31677         this.el.setStyle("z-index", 20000);
31678         this.collapsedEl.hide({
31679             duration:.1
31680         });
31681         this.el.slideIn(this.getSlideAnchor(), {
31682             callback : function(){
31683                 this.el.setStyle("z-index", "");
31684                 this.afterSlide();
31685                 if(this.split){
31686                     this.split.el.show();
31687                 }
31688                 this.fireEvent("invalidated", this);
31689                 this.fireEvent("expanded", this);
31690             },
31691             scope: this,
31692             block: true
31693         });
31694     },
31695
31696     anchors : {
31697         "west" : "left",
31698         "east" : "right",
31699         "north" : "top",
31700         "south" : "bottom"
31701     },
31702
31703     sanchors : {
31704         "west" : "l",
31705         "east" : "r",
31706         "north" : "t",
31707         "south" : "b"
31708     },
31709
31710     canchors : {
31711         "west" : "tl-tr",
31712         "east" : "tr-tl",
31713         "north" : "tl-bl",
31714         "south" : "bl-tl"
31715     },
31716
31717     getAnchor : function(){
31718         return this.anchors[this.position];
31719     },
31720
31721     getCollapseAnchor : function(){
31722         return this.canchors[this.position];
31723     },
31724
31725     getSlideAnchor : function(){
31726         return this.sanchors[this.position];
31727     },
31728
31729     getAlignAdj : function(){
31730         var cm = this.cmargins;
31731         switch(this.position){
31732             case "west":
31733                 return [0, 0];
31734             break;
31735             case "east":
31736                 return [0, 0];
31737             break;
31738             case "north":
31739                 return [0, 0];
31740             break;
31741             case "south":
31742                 return [0, 0];
31743             break;
31744         }
31745     },
31746
31747     getExpandAdj : function(){
31748         var c = this.collapsedEl, cm = this.cmargins;
31749         switch(this.position){
31750             case "west":
31751                 return [-(cm.right+c.getWidth()+cm.left), 0];
31752             break;
31753             case "east":
31754                 return [cm.right+c.getWidth()+cm.left, 0];
31755             break;
31756             case "north":
31757                 return [0, -(cm.top+cm.bottom+c.getHeight())];
31758             break;
31759             case "south":
31760                 return [0, cm.top+cm.bottom+c.getHeight()];
31761             break;
31762         }
31763     }
31764 });/*
31765  * Based on:
31766  * Ext JS Library 1.1.1
31767  * Copyright(c) 2006-2007, Ext JS, LLC.
31768  *
31769  * Originally Released Under LGPL - original licence link has changed is not relivant.
31770  *
31771  * Fork - LGPL
31772  * <script type="text/javascript">
31773  */
31774 /*
31775  * These classes are private internal classes
31776  */
31777 Roo.CenterLayoutRegion = function(mgr, config){
31778     Roo.LayoutRegion.call(this, mgr, config, "center");
31779     this.visible = true;
31780     this.minWidth = config.minWidth || 20;
31781     this.minHeight = config.minHeight || 20;
31782 };
31783
31784 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
31785     hide : function(){
31786         // center panel can't be hidden
31787     },
31788     
31789     show : function(){
31790         // center panel can't be hidden
31791     },
31792     
31793     getMinWidth: function(){
31794         return this.minWidth;
31795     },
31796     
31797     getMinHeight: function(){
31798         return this.minHeight;
31799     }
31800 });
31801
31802
31803 Roo.NorthLayoutRegion = function(mgr, config){
31804     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
31805     if(this.split){
31806         this.split.placement = Roo.SplitBar.TOP;
31807         this.split.orientation = Roo.SplitBar.VERTICAL;
31808         this.split.el.addClass("x-layout-split-v");
31809     }
31810     var size = config.initialSize || config.height;
31811     if(typeof size != "undefined"){
31812         this.el.setHeight(size);
31813     }
31814 };
31815 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
31816     orientation: Roo.SplitBar.VERTICAL,
31817     getBox : function(){
31818         if(this.collapsed){
31819             return this.collapsedEl.getBox();
31820         }
31821         var box = this.el.getBox();
31822         if(this.split){
31823             box.height += this.split.el.getHeight();
31824         }
31825         return box;
31826     },
31827     
31828     updateBox : function(box){
31829         if(this.split && !this.collapsed){
31830             box.height -= this.split.el.getHeight();
31831             this.split.el.setLeft(box.x);
31832             this.split.el.setTop(box.y+box.height);
31833             this.split.el.setWidth(box.width);
31834         }
31835         if(this.collapsed){
31836             this.updateBody(box.width, null);
31837         }
31838         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31839     }
31840 });
31841
31842 Roo.SouthLayoutRegion = function(mgr, config){
31843     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
31844     if(this.split){
31845         this.split.placement = Roo.SplitBar.BOTTOM;
31846         this.split.orientation = Roo.SplitBar.VERTICAL;
31847         this.split.el.addClass("x-layout-split-v");
31848     }
31849     var size = config.initialSize || config.height;
31850     if(typeof size != "undefined"){
31851         this.el.setHeight(size);
31852     }
31853 };
31854 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
31855     orientation: Roo.SplitBar.VERTICAL,
31856     getBox : function(){
31857         if(this.collapsed){
31858             return this.collapsedEl.getBox();
31859         }
31860         var box = this.el.getBox();
31861         if(this.split){
31862             var sh = this.split.el.getHeight();
31863             box.height += sh;
31864             box.y -= sh;
31865         }
31866         return box;
31867     },
31868     
31869     updateBox : function(box){
31870         if(this.split && !this.collapsed){
31871             var sh = this.split.el.getHeight();
31872             box.height -= sh;
31873             box.y += sh;
31874             this.split.el.setLeft(box.x);
31875             this.split.el.setTop(box.y-sh);
31876             this.split.el.setWidth(box.width);
31877         }
31878         if(this.collapsed){
31879             this.updateBody(box.width, null);
31880         }
31881         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31882     }
31883 });
31884
31885 Roo.EastLayoutRegion = function(mgr, config){
31886     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
31887     if(this.split){
31888         this.split.placement = Roo.SplitBar.RIGHT;
31889         this.split.orientation = Roo.SplitBar.HORIZONTAL;
31890         this.split.el.addClass("x-layout-split-h");
31891     }
31892     var size = config.initialSize || config.width;
31893     if(typeof size != "undefined"){
31894         this.el.setWidth(size);
31895     }
31896 };
31897 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
31898     orientation: Roo.SplitBar.HORIZONTAL,
31899     getBox : function(){
31900         if(this.collapsed){
31901             return this.collapsedEl.getBox();
31902         }
31903         var box = this.el.getBox();
31904         if(this.split){
31905             var sw = this.split.el.getWidth();
31906             box.width += sw;
31907             box.x -= sw;
31908         }
31909         return box;
31910     },
31911
31912     updateBox : function(box){
31913         if(this.split && !this.collapsed){
31914             var sw = this.split.el.getWidth();
31915             box.width -= sw;
31916             this.split.el.setLeft(box.x);
31917             this.split.el.setTop(box.y);
31918             this.split.el.setHeight(box.height);
31919             box.x += sw;
31920         }
31921         if(this.collapsed){
31922             this.updateBody(null, box.height);
31923         }
31924         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31925     }
31926 });
31927
31928 Roo.WestLayoutRegion = function(mgr, config){
31929     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
31930     if(this.split){
31931         this.split.placement = Roo.SplitBar.LEFT;
31932         this.split.orientation = Roo.SplitBar.HORIZONTAL;
31933         this.split.el.addClass("x-layout-split-h");
31934     }
31935     var size = config.initialSize || config.width;
31936     if(typeof size != "undefined"){
31937         this.el.setWidth(size);
31938     }
31939 };
31940 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
31941     orientation: Roo.SplitBar.HORIZONTAL,
31942     getBox : function(){
31943         if(this.collapsed){
31944             return this.collapsedEl.getBox();
31945         }
31946         var box = this.el.getBox();
31947         if(this.split){
31948             box.width += this.split.el.getWidth();
31949         }
31950         return box;
31951     },
31952     
31953     updateBox : function(box){
31954         if(this.split && !this.collapsed){
31955             var sw = this.split.el.getWidth();
31956             box.width -= sw;
31957             this.split.el.setLeft(box.x+box.width);
31958             this.split.el.setTop(box.y);
31959             this.split.el.setHeight(box.height);
31960         }
31961         if(this.collapsed){
31962             this.updateBody(null, box.height);
31963         }
31964         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31965     }
31966 });
31967 /*
31968  * Based on:
31969  * Ext JS Library 1.1.1
31970  * Copyright(c) 2006-2007, Ext JS, LLC.
31971  *
31972  * Originally Released Under LGPL - original licence link has changed is not relivant.
31973  *
31974  * Fork - LGPL
31975  * <script type="text/javascript">
31976  */
31977  
31978  
31979 /*
31980  * Private internal class for reading and applying state
31981  */
31982 Roo.LayoutStateManager = function(layout){
31983      // default empty state
31984      this.state = {
31985         north: {},
31986         south: {},
31987         east: {},
31988         west: {}       
31989     };
31990 };
31991
31992 Roo.LayoutStateManager.prototype = {
31993     init : function(layout, provider){
31994         this.provider = provider;
31995         var state = provider.get(layout.id+"-layout-state");
31996         if(state){
31997             var wasUpdating = layout.isUpdating();
31998             if(!wasUpdating){
31999                 layout.beginUpdate();
32000             }
32001             for(var key in state){
32002                 if(typeof state[key] != "function"){
32003                     var rstate = state[key];
32004                     var r = layout.getRegion(key);
32005                     if(r && rstate){
32006                         if(rstate.size){
32007                             r.resizeTo(rstate.size);
32008                         }
32009                         if(rstate.collapsed == true){
32010                             r.collapse(true);
32011                         }else{
32012                             r.expand(null, true);
32013                         }
32014                     }
32015                 }
32016             }
32017             if(!wasUpdating){
32018                 layout.endUpdate();
32019             }
32020             this.state = state; 
32021         }
32022         this.layout = layout;
32023         layout.on("regionresized", this.onRegionResized, this);
32024         layout.on("regioncollapsed", this.onRegionCollapsed, this);
32025         layout.on("regionexpanded", this.onRegionExpanded, this);
32026     },
32027     
32028     storeState : function(){
32029         this.provider.set(this.layout.id+"-layout-state", this.state);
32030     },
32031     
32032     onRegionResized : function(region, newSize){
32033         this.state[region.getPosition()].size = newSize;
32034         this.storeState();
32035     },
32036     
32037     onRegionCollapsed : function(region){
32038         this.state[region.getPosition()].collapsed = true;
32039         this.storeState();
32040     },
32041     
32042     onRegionExpanded : function(region){
32043         this.state[region.getPosition()].collapsed = false;
32044         this.storeState();
32045     }
32046 };/*
32047  * Based on:
32048  * Ext JS Library 1.1.1
32049  * Copyright(c) 2006-2007, Ext JS, LLC.
32050  *
32051  * Originally Released Under LGPL - original licence link has changed is not relivant.
32052  *
32053  * Fork - LGPL
32054  * <script type="text/javascript">
32055  */
32056 /**
32057  * @class Roo.ContentPanel
32058  * @extends Roo.util.Observable
32059  * A basic ContentPanel element.
32060  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
32061  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
32062  * @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
32063  * @cfg {Boolean}   closable      True if the panel can be closed/removed
32064  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
32065  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
32066  * @cfg {Toolbar}   toolbar       A toolbar for this panel
32067  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
32068  * @cfg {String} title          The title for this panel
32069  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
32070  * @cfg {String} url            Calls {@link #setUrl} with this value
32071  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
32072  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
32073  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
32074  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
32075
32076  * @constructor
32077  * Create a new ContentPanel.
32078  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
32079  * @param {String/Object} config A string to set only the title or a config object
32080  * @param {String} content (optional) Set the HTML content for this panel
32081  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
32082  */
32083 Roo.ContentPanel = function(el, config, content){
32084     
32085      
32086     /*
32087     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
32088         config = el;
32089         el = Roo.id();
32090     }
32091     if (config && config.parentLayout) { 
32092         el = config.parentLayout.el.createChild(); 
32093     }
32094     */
32095     if(el.autoCreate){ // xtype is available if this is called from factory
32096         config = el;
32097         el = Roo.id();
32098     }
32099     this.el = Roo.get(el);
32100     if(!this.el && config && config.autoCreate){
32101         if(typeof config.autoCreate == "object"){
32102             if(!config.autoCreate.id){
32103                 config.autoCreate.id = config.id||el;
32104             }
32105             this.el = Roo.DomHelper.append(document.body,
32106                         config.autoCreate, true);
32107         }else{
32108             this.el = Roo.DomHelper.append(document.body,
32109                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
32110         }
32111     }
32112     this.closable = false;
32113     this.loaded = false;
32114     this.active = false;
32115     if(typeof config == "string"){
32116         this.title = config;
32117     }else{
32118         Roo.apply(this, config);
32119     }
32120     
32121     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
32122         this.wrapEl = this.el.wrap();
32123         this.toolbar.container = this.el.insertSibling(false, 'before');
32124         this.toolbar = new Roo.Toolbar(this.toolbar);
32125     }
32126     
32127     
32128     
32129     if(this.resizeEl){
32130         this.resizeEl = Roo.get(this.resizeEl, true);
32131     }else{
32132         this.resizeEl = this.el;
32133     }
32134     this.addEvents({
32135         /**
32136          * @event activate
32137          * Fires when this panel is activated. 
32138          * @param {Roo.ContentPanel} this
32139          */
32140         "activate" : true,
32141         /**
32142          * @event deactivate
32143          * Fires when this panel is activated. 
32144          * @param {Roo.ContentPanel} this
32145          */
32146         "deactivate" : true,
32147
32148         /**
32149          * @event resize
32150          * Fires when this panel is resized if fitToFrame is true.
32151          * @param {Roo.ContentPanel} this
32152          * @param {Number} width The width after any component adjustments
32153          * @param {Number} height The height after any component adjustments
32154          */
32155         "resize" : true,
32156         
32157          /**
32158          * @event render
32159          * Fires when this tab is created
32160          * @param {Roo.ContentPanel} this
32161          */
32162         "render" : true
32163         
32164         
32165         
32166     });
32167     if(this.autoScroll){
32168         this.resizeEl.setStyle("overflow", "auto");
32169     } else {
32170         // fix randome scrolling
32171         this.el.on('scroll', function() {
32172             Roo.log('fix random scolling');
32173             this.scrollTo('top',0); 
32174         });
32175     }
32176     content = content || this.content;
32177     if(content){
32178         this.setContent(content);
32179     }
32180     if(config && config.url){
32181         this.setUrl(this.url, this.params, this.loadOnce);
32182     }
32183     
32184     
32185     
32186     Roo.ContentPanel.superclass.constructor.call(this);
32187     
32188     this.fireEvent('render', this);
32189 };
32190
32191 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
32192     tabTip:'',
32193     setRegion : function(region){
32194         this.region = region;
32195         if(region){
32196            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
32197         }else{
32198            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
32199         } 
32200     },
32201     
32202     /**
32203      * Returns the toolbar for this Panel if one was configured. 
32204      * @return {Roo.Toolbar} 
32205      */
32206     getToolbar : function(){
32207         return this.toolbar;
32208     },
32209     
32210     setActiveState : function(active){
32211         this.active = active;
32212         if(!active){
32213             this.fireEvent("deactivate", this);
32214         }else{
32215             this.fireEvent("activate", this);
32216         }
32217     },
32218     /**
32219      * Updates this panel's element
32220      * @param {String} content The new content
32221      * @param {Boolean} loadScripts (optional) true to look for and process scripts
32222     */
32223     setContent : function(content, loadScripts){
32224         this.el.update(content, loadScripts);
32225     },
32226
32227     ignoreResize : function(w, h){
32228         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
32229             return true;
32230         }else{
32231             this.lastSize = {width: w, height: h};
32232             return false;
32233         }
32234     },
32235     /**
32236      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
32237      * @return {Roo.UpdateManager} The UpdateManager
32238      */
32239     getUpdateManager : function(){
32240         return this.el.getUpdateManager();
32241     },
32242      /**
32243      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
32244      * @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:
32245 <pre><code>
32246 panel.load({
32247     url: "your-url.php",
32248     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
32249     callback: yourFunction,
32250     scope: yourObject, //(optional scope)
32251     discardUrl: false,
32252     nocache: false,
32253     text: "Loading...",
32254     timeout: 30,
32255     scripts: false
32256 });
32257 </code></pre>
32258      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
32259      * 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.
32260      * @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}
32261      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
32262      * @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.
32263      * @return {Roo.ContentPanel} this
32264      */
32265     load : function(){
32266         var um = this.el.getUpdateManager();
32267         um.update.apply(um, arguments);
32268         return this;
32269     },
32270
32271
32272     /**
32273      * 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.
32274      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
32275      * @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)
32276      * @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)
32277      * @return {Roo.UpdateManager} The UpdateManager
32278      */
32279     setUrl : function(url, params, loadOnce){
32280         if(this.refreshDelegate){
32281             this.removeListener("activate", this.refreshDelegate);
32282         }
32283         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
32284         this.on("activate", this.refreshDelegate);
32285         return this.el.getUpdateManager();
32286     },
32287     
32288     _handleRefresh : function(url, params, loadOnce){
32289         if(!loadOnce || !this.loaded){
32290             var updater = this.el.getUpdateManager();
32291             updater.update(url, params, this._setLoaded.createDelegate(this));
32292         }
32293     },
32294     
32295     _setLoaded : function(){
32296         this.loaded = true;
32297     }, 
32298     
32299     /**
32300      * Returns this panel's id
32301      * @return {String} 
32302      */
32303     getId : function(){
32304         return this.el.id;
32305     },
32306     
32307     /** 
32308      * Returns this panel's element - used by regiosn to add.
32309      * @return {Roo.Element} 
32310      */
32311     getEl : function(){
32312         return this.wrapEl || this.el;
32313     },
32314     
32315     adjustForComponents : function(width, height){
32316         if(this.resizeEl != this.el){
32317             width -= this.el.getFrameWidth('lr');
32318             height -= this.el.getFrameWidth('tb');
32319         }
32320         if(this.toolbar){
32321             var te = this.toolbar.getEl();
32322             height -= te.getHeight();
32323             te.setWidth(width);
32324         }
32325         if(this.adjustments){
32326             width += this.adjustments[0];
32327             height += this.adjustments[1];
32328         }
32329         return {"width": width, "height": height};
32330     },
32331     
32332     setSize : function(width, height){
32333         if(this.fitToFrame && !this.ignoreResize(width, height)){
32334             if(this.fitContainer && this.resizeEl != this.el){
32335                 this.el.setSize(width, height);
32336             }
32337             var size = this.adjustForComponents(width, height);
32338             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
32339             this.fireEvent('resize', this, size.width, size.height);
32340         }
32341     },
32342     
32343     /**
32344      * Returns this panel's title
32345      * @return {String} 
32346      */
32347     getTitle : function(){
32348         return this.title;
32349     },
32350     
32351     /**
32352      * Set this panel's title
32353      * @param {String} title
32354      */
32355     setTitle : function(title){
32356         this.title = title;
32357         if(this.region){
32358             this.region.updatePanelTitle(this, title);
32359         }
32360     },
32361     
32362     /**
32363      * Returns true is this panel was configured to be closable
32364      * @return {Boolean} 
32365      */
32366     isClosable : function(){
32367         return this.closable;
32368     },
32369     
32370     beforeSlide : function(){
32371         this.el.clip();
32372         this.resizeEl.clip();
32373     },
32374     
32375     afterSlide : function(){
32376         this.el.unclip();
32377         this.resizeEl.unclip();
32378     },
32379     
32380     /**
32381      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
32382      *   Will fail silently if the {@link #setUrl} method has not been called.
32383      *   This does not activate the panel, just updates its content.
32384      */
32385     refresh : function(){
32386         if(this.refreshDelegate){
32387            this.loaded = false;
32388            this.refreshDelegate();
32389         }
32390     },
32391     
32392     /**
32393      * Destroys this panel
32394      */
32395     destroy : function(){
32396         this.el.removeAllListeners();
32397         var tempEl = document.createElement("span");
32398         tempEl.appendChild(this.el.dom);
32399         tempEl.innerHTML = "";
32400         this.el.remove();
32401         this.el = null;
32402     },
32403     
32404     /**
32405      * form - if the content panel contains a form - this is a reference to it.
32406      * @type {Roo.form.Form}
32407      */
32408     form : false,
32409     /**
32410      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
32411      *    This contains a reference to it.
32412      * @type {Roo.View}
32413      */
32414     view : false,
32415     
32416       /**
32417      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
32418      * <pre><code>
32419
32420 layout.addxtype({
32421        xtype : 'Form',
32422        items: [ .... ]
32423    }
32424 );
32425
32426 </code></pre>
32427      * @param {Object} cfg Xtype definition of item to add.
32428      */
32429     
32430     addxtype : function(cfg) {
32431         // add form..
32432         if (cfg.xtype.match(/^Form$/)) {
32433             var el = this.el.createChild();
32434
32435             this.form = new  Roo.form.Form(cfg);
32436             
32437             
32438             if ( this.form.allItems.length) this.form.render(el.dom);
32439             return this.form;
32440         }
32441         // should only have one of theses..
32442         if (['View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
32443             // views..
32444             cfg.el = this.el.appendChild(document.createElement("div"));
32445             // factory?
32446             
32447             var ret = new Roo.factory(cfg);
32448             ret.render && ret.render(false, ''); // render blank..
32449             this.view = ret;
32450             return ret;
32451         }
32452         return false;
32453     }
32454 });
32455
32456 /**
32457  * @class Roo.GridPanel
32458  * @extends Roo.ContentPanel
32459  * @constructor
32460  * Create a new GridPanel.
32461  * @param {Roo.grid.Grid} grid The grid for this panel
32462  * @param {String/Object} config A string to set only the panel's title, or a config object
32463  */
32464 Roo.GridPanel = function(grid, config){
32465     
32466   
32467     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
32468         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
32469         
32470     this.wrapper.dom.appendChild(grid.getGridEl().dom);
32471     
32472     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
32473     
32474     if(this.toolbar){
32475         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
32476     }
32477     // xtype created footer. - not sure if will work as we normally have to render first..
32478     if (this.footer && !this.footer.el && this.footer.xtype) {
32479         
32480         this.footer.container = this.grid.getView().getFooterPanel(true);
32481         this.footer.dataSource = this.grid.dataSource;
32482         this.footer = Roo.factory(this.footer, Roo);
32483         
32484     }
32485     
32486     grid.monitorWindowResize = false; // turn off autosizing
32487     grid.autoHeight = false;
32488     grid.autoWidth = false;
32489     this.grid = grid;
32490     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
32491 };
32492
32493 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
32494     getId : function(){
32495         return this.grid.id;
32496     },
32497     
32498     /**
32499      * Returns the grid for this panel
32500      * @return {Roo.grid.Grid} 
32501      */
32502     getGrid : function(){
32503         return this.grid;    
32504     },
32505     
32506     setSize : function(width, height){
32507         if(!this.ignoreResize(width, height)){
32508             var grid = this.grid;
32509             var size = this.adjustForComponents(width, height);
32510             grid.getGridEl().setSize(size.width, size.height);
32511             grid.autoSize();
32512         }
32513     },
32514     
32515     beforeSlide : function(){
32516         this.grid.getView().scroller.clip();
32517     },
32518     
32519     afterSlide : function(){
32520         this.grid.getView().scroller.unclip();
32521     },
32522     
32523     destroy : function(){
32524         this.grid.destroy();
32525         delete this.grid;
32526         Roo.GridPanel.superclass.destroy.call(this); 
32527     }
32528 });
32529
32530
32531 /**
32532  * @class Roo.NestedLayoutPanel
32533  * @extends Roo.ContentPanel
32534  * @constructor
32535  * Create a new NestedLayoutPanel.
32536  * 
32537  * 
32538  * @param {Roo.BorderLayout} layout The layout for this panel
32539  * @param {String/Object} config A string to set only the title or a config object
32540  */
32541 Roo.NestedLayoutPanel = function(layout, config)
32542 {
32543     // construct with only one argument..
32544     /* FIXME - implement nicer consturctors
32545     if (layout.layout) {
32546         config = layout;
32547         layout = config.layout;
32548         delete config.layout;
32549     }
32550     if (layout.xtype && !layout.getEl) {
32551         // then layout needs constructing..
32552         layout = Roo.factory(layout, Roo);
32553     }
32554     */
32555     
32556     
32557     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
32558     
32559     layout.monitorWindowResize = false; // turn off autosizing
32560     this.layout = layout;
32561     this.layout.getEl().addClass("x-layout-nested-layout");
32562     
32563     
32564     
32565     
32566 };
32567
32568 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
32569
32570     setSize : function(width, height){
32571         if(!this.ignoreResize(width, height)){
32572             var size = this.adjustForComponents(width, height);
32573             var el = this.layout.getEl();
32574             el.setSize(size.width, size.height);
32575             var touch = el.dom.offsetWidth;
32576             this.layout.layout();
32577             // ie requires a double layout on the first pass
32578             if(Roo.isIE && !this.initialized){
32579                 this.initialized = true;
32580                 this.layout.layout();
32581             }
32582         }
32583     },
32584     
32585     // activate all subpanels if not currently active..
32586     
32587     setActiveState : function(active){
32588         this.active = active;
32589         if(!active){
32590             this.fireEvent("deactivate", this);
32591             return;
32592         }
32593         
32594         this.fireEvent("activate", this);
32595         // not sure if this should happen before or after..
32596         if (!this.layout) {
32597             return; // should not happen..
32598         }
32599         var reg = false;
32600         for (var r in this.layout.regions) {
32601             reg = this.layout.getRegion(r);
32602             if (reg.getActivePanel()) {
32603                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
32604                 reg.setActivePanel(reg.getActivePanel());
32605                 continue;
32606             }
32607             if (!reg.panels.length) {
32608                 continue;
32609             }
32610             reg.showPanel(reg.getPanel(0));
32611         }
32612         
32613         
32614         
32615         
32616     },
32617     
32618     /**
32619      * Returns the nested BorderLayout for this panel
32620      * @return {Roo.BorderLayout} 
32621      */
32622     getLayout : function(){
32623         return this.layout;
32624     },
32625     
32626      /**
32627      * Adds a xtype elements to the layout of the nested panel
32628      * <pre><code>
32629
32630 panel.addxtype({
32631        xtype : 'ContentPanel',
32632        region: 'west',
32633        items: [ .... ]
32634    }
32635 );
32636
32637 panel.addxtype({
32638         xtype : 'NestedLayoutPanel',
32639         region: 'west',
32640         layout: {
32641            center: { },
32642            west: { }   
32643         },
32644         items : [ ... list of content panels or nested layout panels.. ]
32645    }
32646 );
32647 </code></pre>
32648      * @param {Object} cfg Xtype definition of item to add.
32649      */
32650     addxtype : function(cfg) {
32651         return this.layout.addxtype(cfg);
32652     
32653     }
32654 });
32655
32656 Roo.ScrollPanel = function(el, config, content){
32657     config = config || {};
32658     config.fitToFrame = true;
32659     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
32660     
32661     this.el.dom.style.overflow = "hidden";
32662     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
32663     this.el.removeClass("x-layout-inactive-content");
32664     this.el.on("mousewheel", this.onWheel, this);
32665
32666     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
32667     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
32668     up.unselectable(); down.unselectable();
32669     up.on("click", this.scrollUp, this);
32670     down.on("click", this.scrollDown, this);
32671     up.addClassOnOver("x-scroller-btn-over");
32672     down.addClassOnOver("x-scroller-btn-over");
32673     up.addClassOnClick("x-scroller-btn-click");
32674     down.addClassOnClick("x-scroller-btn-click");
32675     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
32676
32677     this.resizeEl = this.el;
32678     this.el = wrap; this.up = up; this.down = down;
32679 };
32680
32681 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
32682     increment : 100,
32683     wheelIncrement : 5,
32684     scrollUp : function(){
32685         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
32686     },
32687
32688     scrollDown : function(){
32689         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
32690     },
32691
32692     afterScroll : function(){
32693         var el = this.resizeEl;
32694         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
32695         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
32696         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
32697     },
32698
32699     setSize : function(){
32700         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
32701         this.afterScroll();
32702     },
32703
32704     onWheel : function(e){
32705         var d = e.getWheelDelta();
32706         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
32707         this.afterScroll();
32708         e.stopEvent();
32709     },
32710
32711     setContent : function(content, loadScripts){
32712         this.resizeEl.update(content, loadScripts);
32713     }
32714
32715 });
32716
32717
32718
32719
32720
32721
32722
32723
32724
32725 /**
32726  * @class Roo.TreePanel
32727  * @extends Roo.ContentPanel
32728  * @constructor
32729  * Create a new TreePanel. - defaults to fit/scoll contents.
32730  * @param {String/Object} config A string to set only the panel's title, or a config object
32731  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
32732  */
32733 Roo.TreePanel = function(config){
32734     var el = config.el;
32735     var tree = config.tree;
32736     delete config.tree; 
32737     delete config.el; // hopefull!
32738     
32739     // wrapper for IE7 strict & safari scroll issue
32740     
32741     var treeEl = el.createChild();
32742     config.resizeEl = treeEl;
32743     
32744     
32745     
32746     Roo.TreePanel.superclass.constructor.call(this, el, config);
32747  
32748  
32749     this.tree = new Roo.tree.TreePanel(treeEl , tree);
32750     //console.log(tree);
32751     this.on('activate', function()
32752     {
32753         if (this.tree.rendered) {
32754             return;
32755         }
32756         //console.log('render tree');
32757         this.tree.render();
32758     });
32759     
32760     this.on('resize',  function (cp, w, h) {
32761             this.tree.innerCt.setWidth(w);
32762             this.tree.innerCt.setHeight(h);
32763             this.tree.innerCt.setStyle('overflow-y', 'auto');
32764     });
32765
32766         
32767     
32768 };
32769
32770 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
32771     fitToFrame : true,
32772     autoScroll : true
32773 });
32774
32775
32776
32777
32778
32779
32780
32781
32782
32783
32784
32785 /*
32786  * Based on:
32787  * Ext JS Library 1.1.1
32788  * Copyright(c) 2006-2007, Ext JS, LLC.
32789  *
32790  * Originally Released Under LGPL - original licence link has changed is not relivant.
32791  *
32792  * Fork - LGPL
32793  * <script type="text/javascript">
32794  */
32795  
32796
32797 /**
32798  * @class Roo.ReaderLayout
32799  * @extends Roo.BorderLayout
32800  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
32801  * center region containing two nested regions (a top one for a list view and one for item preview below),
32802  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
32803  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
32804  * expedites the setup of the overall layout and regions for this common application style.
32805  * Example:
32806  <pre><code>
32807 var reader = new Roo.ReaderLayout();
32808 var CP = Roo.ContentPanel;  // shortcut for adding
32809
32810 reader.beginUpdate();
32811 reader.add("north", new CP("north", "North"));
32812 reader.add("west", new CP("west", {title: "West"}));
32813 reader.add("east", new CP("east", {title: "East"}));
32814
32815 reader.regions.listView.add(new CP("listView", "List"));
32816 reader.regions.preview.add(new CP("preview", "Preview"));
32817 reader.endUpdate();
32818 </code></pre>
32819 * @constructor
32820 * Create a new ReaderLayout
32821 * @param {Object} config Configuration options
32822 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
32823 * document.body if omitted)
32824 */
32825 Roo.ReaderLayout = function(config, renderTo){
32826     var c = config || {size:{}};
32827     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
32828         north: c.north !== false ? Roo.apply({
32829             split:false,
32830             initialSize: 32,
32831             titlebar: false
32832         }, c.north) : false,
32833         west: c.west !== false ? Roo.apply({
32834             split:true,
32835             initialSize: 200,
32836             minSize: 175,
32837             maxSize: 400,
32838             titlebar: true,
32839             collapsible: true,
32840             animate: true,
32841             margins:{left:5,right:0,bottom:5,top:5},
32842             cmargins:{left:5,right:5,bottom:5,top:5}
32843         }, c.west) : false,
32844         east: c.east !== false ? Roo.apply({
32845             split:true,
32846             initialSize: 200,
32847             minSize: 175,
32848             maxSize: 400,
32849             titlebar: true,
32850             collapsible: true,
32851             animate: true,
32852             margins:{left:0,right:5,bottom:5,top:5},
32853             cmargins:{left:5,right:5,bottom:5,top:5}
32854         }, c.east) : false,
32855         center: Roo.apply({
32856             tabPosition: 'top',
32857             autoScroll:false,
32858             closeOnTab: true,
32859             titlebar:false,
32860             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
32861         }, c.center)
32862     });
32863
32864     this.el.addClass('x-reader');
32865
32866     this.beginUpdate();
32867
32868     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
32869         south: c.preview !== false ? Roo.apply({
32870             split:true,
32871             initialSize: 200,
32872             minSize: 100,
32873             autoScroll:true,
32874             collapsible:true,
32875             titlebar: true,
32876             cmargins:{top:5,left:0, right:0, bottom:0}
32877         }, c.preview) : false,
32878         center: Roo.apply({
32879             autoScroll:false,
32880             titlebar:false,
32881             minHeight:200
32882         }, c.listView)
32883     });
32884     this.add('center', new Roo.NestedLayoutPanel(inner,
32885             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
32886
32887     this.endUpdate();
32888
32889     this.regions.preview = inner.getRegion('south');
32890     this.regions.listView = inner.getRegion('center');
32891 };
32892
32893 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
32894  * Based on:
32895  * Ext JS Library 1.1.1
32896  * Copyright(c) 2006-2007, Ext JS, LLC.
32897  *
32898  * Originally Released Under LGPL - original licence link has changed is not relivant.
32899  *
32900  * Fork - LGPL
32901  * <script type="text/javascript">
32902  */
32903  
32904 /**
32905  * @class Roo.grid.Grid
32906  * @extends Roo.util.Observable
32907  * This class represents the primary interface of a component based grid control.
32908  * <br><br>Usage:<pre><code>
32909  var grid = new Roo.grid.Grid("my-container-id", {
32910      ds: myDataStore,
32911      cm: myColModel,
32912      selModel: mySelectionModel,
32913      autoSizeColumns: true,
32914      monitorWindowResize: false,
32915      trackMouseOver: true
32916  });
32917  // set any options
32918  grid.render();
32919  * </code></pre>
32920  * <b>Common Problems:</b><br/>
32921  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
32922  * element will correct this<br/>
32923  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
32924  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
32925  * are unpredictable.<br/>
32926  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
32927  * grid to calculate dimensions/offsets.<br/>
32928   * @constructor
32929  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
32930  * The container MUST have some type of size defined for the grid to fill. The container will be
32931  * automatically set to position relative if it isn't already.
32932  * @param {Object} config A config object that sets properties on this grid.
32933  */
32934 Roo.grid.Grid = function(container, config){
32935         // initialize the container
32936         this.container = Roo.get(container);
32937         this.container.update("");
32938         this.container.setStyle("overflow", "hidden");
32939     this.container.addClass('x-grid-container');
32940
32941     this.id = this.container.id;
32942
32943     Roo.apply(this, config);
32944     // check and correct shorthanded configs
32945     if(this.ds){
32946         this.dataSource = this.ds;
32947         delete this.ds;
32948     }
32949     if(this.cm){
32950         this.colModel = this.cm;
32951         delete this.cm;
32952     }
32953     if(this.sm){
32954         this.selModel = this.sm;
32955         delete this.sm;
32956     }
32957
32958     if (this.selModel) {
32959         this.selModel = Roo.factory(this.selModel, Roo.grid);
32960         this.sm = this.selModel;
32961         this.sm.xmodule = this.xmodule || false;
32962     }
32963     if (typeof(this.colModel.config) == 'undefined') {
32964         this.colModel = new Roo.grid.ColumnModel(this.colModel);
32965         this.cm = this.colModel;
32966         this.cm.xmodule = this.xmodule || false;
32967     }
32968     if (this.dataSource) {
32969         this.dataSource= Roo.factory(this.dataSource, Roo.data);
32970         this.ds = this.dataSource;
32971         this.ds.xmodule = this.xmodule || false;
32972          
32973     }
32974     
32975     
32976     
32977     if(this.width){
32978         this.container.setWidth(this.width);
32979     }
32980
32981     if(this.height){
32982         this.container.setHeight(this.height);
32983     }
32984     /** @private */
32985         this.addEvents({
32986         // raw events
32987         /**
32988          * @event click
32989          * The raw click event for the entire grid.
32990          * @param {Roo.EventObject} e
32991          */
32992         "click" : true,
32993         /**
32994          * @event dblclick
32995          * The raw dblclick event for the entire grid.
32996          * @param {Roo.EventObject} e
32997          */
32998         "dblclick" : true,
32999         /**
33000          * @event contextmenu
33001          * The raw contextmenu event for the entire grid.
33002          * @param {Roo.EventObject} e
33003          */
33004         "contextmenu" : true,
33005         /**
33006          * @event mousedown
33007          * The raw mousedown event for the entire grid.
33008          * @param {Roo.EventObject} e
33009          */
33010         "mousedown" : true,
33011         /**
33012          * @event mouseup
33013          * The raw mouseup event for the entire grid.
33014          * @param {Roo.EventObject} e
33015          */
33016         "mouseup" : true,
33017         /**
33018          * @event mouseover
33019          * The raw mouseover event for the entire grid.
33020          * @param {Roo.EventObject} e
33021          */
33022         "mouseover" : true,
33023         /**
33024          * @event mouseout
33025          * The raw mouseout event for the entire grid.
33026          * @param {Roo.EventObject} e
33027          */
33028         "mouseout" : true,
33029         /**
33030          * @event keypress
33031          * The raw keypress event for the entire grid.
33032          * @param {Roo.EventObject} e
33033          */
33034         "keypress" : true,
33035         /**
33036          * @event keydown
33037          * The raw keydown event for the entire grid.
33038          * @param {Roo.EventObject} e
33039          */
33040         "keydown" : true,
33041
33042         // custom events
33043
33044         /**
33045          * @event cellclick
33046          * Fires when a cell is clicked
33047          * @param {Grid} this
33048          * @param {Number} rowIndex
33049          * @param {Number} columnIndex
33050          * @param {Roo.EventObject} e
33051          */
33052         "cellclick" : true,
33053         /**
33054          * @event celldblclick
33055          * Fires when a cell is double clicked
33056          * @param {Grid} this
33057          * @param {Number} rowIndex
33058          * @param {Number} columnIndex
33059          * @param {Roo.EventObject} e
33060          */
33061         "celldblclick" : true,
33062         /**
33063          * @event rowclick
33064          * Fires when a row is clicked
33065          * @param {Grid} this
33066          * @param {Number} rowIndex
33067          * @param {Roo.EventObject} e
33068          */
33069         "rowclick" : true,
33070         /**
33071          * @event rowdblclick
33072          * Fires when a row is double clicked
33073          * @param {Grid} this
33074          * @param {Number} rowIndex
33075          * @param {Roo.EventObject} e
33076          */
33077         "rowdblclick" : true,
33078         /**
33079          * @event headerclick
33080          * Fires when a header is clicked
33081          * @param {Grid} this
33082          * @param {Number} columnIndex
33083          * @param {Roo.EventObject} e
33084          */
33085         "headerclick" : true,
33086         /**
33087          * @event headerdblclick
33088          * Fires when a header cell is double clicked
33089          * @param {Grid} this
33090          * @param {Number} columnIndex
33091          * @param {Roo.EventObject} e
33092          */
33093         "headerdblclick" : true,
33094         /**
33095          * @event rowcontextmenu
33096          * Fires when a row is right clicked
33097          * @param {Grid} this
33098          * @param {Number} rowIndex
33099          * @param {Roo.EventObject} e
33100          */
33101         "rowcontextmenu" : true,
33102         /**
33103          * @event cellcontextmenu
33104          * Fires when a cell is right clicked
33105          * @param {Grid} this
33106          * @param {Number} rowIndex
33107          * @param {Number} cellIndex
33108          * @param {Roo.EventObject} e
33109          */
33110          "cellcontextmenu" : true,
33111         /**
33112          * @event headercontextmenu
33113          * Fires when a header is right clicked
33114          * @param {Grid} this
33115          * @param {Number} columnIndex
33116          * @param {Roo.EventObject} e
33117          */
33118         "headercontextmenu" : true,
33119         /**
33120          * @event bodyscroll
33121          * Fires when the body element is scrolled
33122          * @param {Number} scrollLeft
33123          * @param {Number} scrollTop
33124          */
33125         "bodyscroll" : true,
33126         /**
33127          * @event columnresize
33128          * Fires when the user resizes a column
33129          * @param {Number} columnIndex
33130          * @param {Number} newSize
33131          */
33132         "columnresize" : true,
33133         /**
33134          * @event columnmove
33135          * Fires when the user moves a column
33136          * @param {Number} oldIndex
33137          * @param {Number} newIndex
33138          */
33139         "columnmove" : true,
33140         /**
33141          * @event startdrag
33142          * Fires when row(s) start being dragged
33143          * @param {Grid} this
33144          * @param {Roo.GridDD} dd The drag drop object
33145          * @param {event} e The raw browser event
33146          */
33147         "startdrag" : true,
33148         /**
33149          * @event enddrag
33150          * Fires when a drag operation is complete
33151          * @param {Grid} this
33152          * @param {Roo.GridDD} dd The drag drop object
33153          * @param {event} e The raw browser event
33154          */
33155         "enddrag" : true,
33156         /**
33157          * @event dragdrop
33158          * Fires when dragged row(s) are dropped on a valid DD target
33159          * @param {Grid} this
33160          * @param {Roo.GridDD} dd The drag drop object
33161          * @param {String} targetId The target drag drop object
33162          * @param {event} e The raw browser event
33163          */
33164         "dragdrop" : true,
33165         /**
33166          * @event dragover
33167          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
33168          * @param {Grid} this
33169          * @param {Roo.GridDD} dd The drag drop object
33170          * @param {String} targetId The target drag drop object
33171          * @param {event} e The raw browser event
33172          */
33173         "dragover" : true,
33174         /**
33175          * @event dragenter
33176          *  Fires when the dragged row(s) first cross another DD target while being dragged
33177          * @param {Grid} this
33178          * @param {Roo.GridDD} dd The drag drop object
33179          * @param {String} targetId The target drag drop object
33180          * @param {event} e The raw browser event
33181          */
33182         "dragenter" : true,
33183         /**
33184          * @event dragout
33185          * Fires when the dragged row(s) leave another DD target while being dragged
33186          * @param {Grid} this
33187          * @param {Roo.GridDD} dd The drag drop object
33188          * @param {String} targetId The target drag drop object
33189          * @param {event} e The raw browser event
33190          */
33191         "dragout" : true,
33192         /**
33193          * @event rowclass
33194          * Fires when a row is rendered, so you can change add a style to it.
33195          * @param {GridView} gridview   The grid view
33196          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
33197          */
33198         'rowclass' : true,
33199
33200         /**
33201          * @event render
33202          * Fires when the grid is rendered
33203          * @param {Grid} grid
33204          */
33205         'render' : true
33206     });
33207
33208     Roo.grid.Grid.superclass.constructor.call(this);
33209 };
33210 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
33211     
33212     /**
33213      * @cfg {String} ddGroup - drag drop group.
33214      */
33215
33216     /**
33217      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
33218      */
33219     minColumnWidth : 25,
33220
33221     /**
33222      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
33223      * <b>on initial render.</b> It is more efficient to explicitly size the columns
33224      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
33225      */
33226     autoSizeColumns : false,
33227
33228     /**
33229      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
33230      */
33231     autoSizeHeaders : true,
33232
33233     /**
33234      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
33235      */
33236     monitorWindowResize : true,
33237
33238     /**
33239      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
33240      * rows measured to get a columns size. Default is 0 (all rows).
33241      */
33242     maxRowsToMeasure : 0,
33243
33244     /**
33245      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
33246      */
33247     trackMouseOver : true,
33248
33249     /**
33250     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
33251     */
33252     
33253     /**
33254     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
33255     */
33256     enableDragDrop : false,
33257     
33258     /**
33259     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
33260     */
33261     enableColumnMove : true,
33262     
33263     /**
33264     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
33265     */
33266     enableColumnHide : true,
33267     
33268     /**
33269     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
33270     */
33271     enableRowHeightSync : false,
33272     
33273     /**
33274     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
33275     */
33276     stripeRows : true,
33277     
33278     /**
33279     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
33280     */
33281     autoHeight : false,
33282
33283     /**
33284      * @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.
33285      */
33286     autoExpandColumn : false,
33287
33288     /**
33289     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
33290     * Default is 50.
33291     */
33292     autoExpandMin : 50,
33293
33294     /**
33295     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
33296     */
33297     autoExpandMax : 1000,
33298
33299     /**
33300     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
33301     */
33302     view : null,
33303
33304     /**
33305     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
33306     */
33307     loadMask : false,
33308     /**
33309     * @cfg {Roo.dd.DropTarget} dragTarget An {@link Roo.dd.DragTarget} config
33310     */
33311     dropTarget: false,
33312     
33313    
33314     
33315     // private
33316     rendered : false,
33317
33318     /**
33319     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
33320     * of a fixed width. Default is false.
33321     */
33322     /**
33323     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
33324     */
33325     /**
33326      * Called once after all setup has been completed and the grid is ready to be rendered.
33327      * @return {Roo.grid.Grid} this
33328      */
33329     render : function()
33330     {
33331         var c = this.container;
33332         // try to detect autoHeight/width mode
33333         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
33334             this.autoHeight = true;
33335         }
33336         var view = this.getView();
33337         view.init(this);
33338
33339         c.on("click", this.onClick, this);
33340         c.on("dblclick", this.onDblClick, this);
33341         c.on("contextmenu", this.onContextMenu, this);
33342         c.on("keydown", this.onKeyDown, this);
33343
33344         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
33345
33346         this.getSelectionModel().init(this);
33347
33348         view.render();
33349
33350         if(this.loadMask){
33351             this.loadMask = new Roo.LoadMask(this.container,
33352                     Roo.apply({store:this.dataSource}, this.loadMask));
33353         }
33354         
33355         
33356         if (this.toolbar && this.toolbar.xtype) {
33357             this.toolbar.container = this.getView().getHeaderPanel(true);
33358             this.toolbar = new Roo.Toolbar(this.toolbar);
33359         }
33360         if (this.footer && this.footer.xtype) {
33361             this.footer.dataSource = this.getDataSource();
33362             this.footer.container = this.getView().getFooterPanel(true);
33363             this.footer = Roo.factory(this.footer, Roo);
33364         }
33365         if (this.dropTarget && this.dropTarget.xtype) {
33366             delete this.dropTarget.xtype;
33367             this.dropTarget =  new Ext.dd.DropTarget(this.getView().mainBody, this.dropTarget);
33368         }
33369         
33370         
33371         this.rendered = true;
33372         this.fireEvent('render', this);
33373         return this;
33374     },
33375
33376         /**
33377          * Reconfigures the grid to use a different Store and Column Model.
33378          * The View will be bound to the new objects and refreshed.
33379          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
33380          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
33381          */
33382     reconfigure : function(dataSource, colModel){
33383         if(this.loadMask){
33384             this.loadMask.destroy();
33385             this.loadMask = new Roo.LoadMask(this.container,
33386                     Roo.apply({store:dataSource}, this.loadMask));
33387         }
33388         this.view.bind(dataSource, colModel);
33389         this.dataSource = dataSource;
33390         this.colModel = colModel;
33391         this.view.refresh(true);
33392     },
33393
33394     // private
33395     onKeyDown : function(e){
33396         this.fireEvent("keydown", e);
33397     },
33398
33399     /**
33400      * Destroy this grid.
33401      * @param {Boolean} removeEl True to remove the element
33402      */
33403     destroy : function(removeEl, keepListeners){
33404         if(this.loadMask){
33405             this.loadMask.destroy();
33406         }
33407         var c = this.container;
33408         c.removeAllListeners();
33409         this.view.destroy();
33410         this.colModel.purgeListeners();
33411         if(!keepListeners){
33412             this.purgeListeners();
33413         }
33414         c.update("");
33415         if(removeEl === true){
33416             c.remove();
33417         }
33418     },
33419
33420     // private
33421     processEvent : function(name, e){
33422         this.fireEvent(name, e);
33423         var t = e.getTarget();
33424         var v = this.view;
33425         var header = v.findHeaderIndex(t);
33426         if(header !== false){
33427             this.fireEvent("header" + name, this, header, e);
33428         }else{
33429             var row = v.findRowIndex(t);
33430             var cell = v.findCellIndex(t);
33431             if(row !== false){
33432                 this.fireEvent("row" + name, this, row, e);
33433                 if(cell !== false){
33434                     this.fireEvent("cell" + name, this, row, cell, e);
33435                 }
33436             }
33437         }
33438     },
33439
33440     // private
33441     onClick : function(e){
33442         this.processEvent("click", e);
33443     },
33444
33445     // private
33446     onContextMenu : function(e, t){
33447         this.processEvent("contextmenu", e);
33448     },
33449
33450     // private
33451     onDblClick : function(e){
33452         this.processEvent("dblclick", e);
33453     },
33454
33455     // private
33456     walkCells : function(row, col, step, fn, scope){
33457         var cm = this.colModel, clen = cm.getColumnCount();
33458         var ds = this.dataSource, rlen = ds.getCount(), first = true;
33459         if(step < 0){
33460             if(col < 0){
33461                 row--;
33462                 first = false;
33463             }
33464             while(row >= 0){
33465                 if(!first){
33466                     col = clen-1;
33467                 }
33468                 first = false;
33469                 while(col >= 0){
33470                     if(fn.call(scope || this, row, col, cm) === true){
33471                         return [row, col];
33472                     }
33473                     col--;
33474                 }
33475                 row--;
33476             }
33477         } else {
33478             if(col >= clen){
33479                 row++;
33480                 first = false;
33481             }
33482             while(row < rlen){
33483                 if(!first){
33484                     col = 0;
33485                 }
33486                 first = false;
33487                 while(col < clen){
33488                     if(fn.call(scope || this, row, col, cm) === true){
33489                         return [row, col];
33490                     }
33491                     col++;
33492                 }
33493                 row++;
33494             }
33495         }
33496         return null;
33497     },
33498
33499     // private
33500     getSelections : function(){
33501         return this.selModel.getSelections();
33502     },
33503
33504     /**
33505      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
33506      * but if manual update is required this method will initiate it.
33507      */
33508     autoSize : function(){
33509         if(this.rendered){
33510             this.view.layout();
33511             if(this.view.adjustForScroll){
33512                 this.view.adjustForScroll();
33513             }
33514         }
33515     },
33516
33517     /**
33518      * Returns the grid's underlying element.
33519      * @return {Element} The element
33520      */
33521     getGridEl : function(){
33522         return this.container;
33523     },
33524
33525     // private for compatibility, overridden by editor grid
33526     stopEditing : function(){},
33527
33528     /**
33529      * Returns the grid's SelectionModel.
33530      * @return {SelectionModel}
33531      */
33532     getSelectionModel : function(){
33533         if(!this.selModel){
33534             this.selModel = new Roo.grid.RowSelectionModel();
33535         }
33536         return this.selModel;
33537     },
33538
33539     /**
33540      * Returns the grid's DataSource.
33541      * @return {DataSource}
33542      */
33543     getDataSource : function(){
33544         return this.dataSource;
33545     },
33546
33547     /**
33548      * Returns the grid's ColumnModel.
33549      * @return {ColumnModel}
33550      */
33551     getColumnModel : function(){
33552         return this.colModel;
33553     },
33554
33555     /**
33556      * Returns the grid's GridView object.
33557      * @return {GridView}
33558      */
33559     getView : function(){
33560         if(!this.view){
33561             this.view = new Roo.grid.GridView(this.viewConfig);
33562         }
33563         return this.view;
33564     },
33565     /**
33566      * Called to get grid's drag proxy text, by default returns this.ddText.
33567      * @return {String}
33568      */
33569     getDragDropText : function(){
33570         var count = this.selModel.getCount();
33571         return String.format(this.ddText, count, count == 1 ? '' : 's');
33572     }
33573 });
33574 /**
33575  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
33576  * %0 is replaced with the number of selected rows.
33577  * @type String
33578  */
33579 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
33580  * Based on:
33581  * Ext JS Library 1.1.1
33582  * Copyright(c) 2006-2007, Ext JS, LLC.
33583  *
33584  * Originally Released Under LGPL - original licence link has changed is not relivant.
33585  *
33586  * Fork - LGPL
33587  * <script type="text/javascript">
33588  */
33589  
33590 Roo.grid.AbstractGridView = function(){
33591         this.grid = null;
33592         
33593         this.events = {
33594             "beforerowremoved" : true,
33595             "beforerowsinserted" : true,
33596             "beforerefresh" : true,
33597             "rowremoved" : true,
33598             "rowsinserted" : true,
33599             "rowupdated" : true,
33600             "refresh" : true
33601         };
33602     Roo.grid.AbstractGridView.superclass.constructor.call(this);
33603 };
33604
33605 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
33606     rowClass : "x-grid-row",
33607     cellClass : "x-grid-cell",
33608     tdClass : "x-grid-td",
33609     hdClass : "x-grid-hd",
33610     splitClass : "x-grid-hd-split",
33611     
33612         init: function(grid){
33613         this.grid = grid;
33614                 var cid = this.grid.getGridEl().id;
33615         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
33616         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
33617         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
33618         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
33619         },
33620         
33621         getColumnRenderers : function(){
33622         var renderers = [];
33623         var cm = this.grid.colModel;
33624         var colCount = cm.getColumnCount();
33625         for(var i = 0; i < colCount; i++){
33626             renderers[i] = cm.getRenderer(i);
33627         }
33628         return renderers;
33629     },
33630     
33631     getColumnIds : function(){
33632         var ids = [];
33633         var cm = this.grid.colModel;
33634         var colCount = cm.getColumnCount();
33635         for(var i = 0; i < colCount; i++){
33636             ids[i] = cm.getColumnId(i);
33637         }
33638         return ids;
33639     },
33640     
33641     getDataIndexes : function(){
33642         if(!this.indexMap){
33643             this.indexMap = this.buildIndexMap();
33644         }
33645         return this.indexMap.colToData;
33646     },
33647     
33648     getColumnIndexByDataIndex : function(dataIndex){
33649         if(!this.indexMap){
33650             this.indexMap = this.buildIndexMap();
33651         }
33652         return this.indexMap.dataToCol[dataIndex];
33653     },
33654     
33655     /**
33656      * Set a css style for a column dynamically. 
33657      * @param {Number} colIndex The index of the column
33658      * @param {String} name The css property name
33659      * @param {String} value The css value
33660      */
33661     setCSSStyle : function(colIndex, name, value){
33662         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
33663         Roo.util.CSS.updateRule(selector, name, value);
33664     },
33665     
33666     generateRules : function(cm){
33667         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
33668         Roo.util.CSS.removeStyleSheet(rulesId);
33669         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33670             var cid = cm.getColumnId(i);
33671             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
33672                          this.tdSelector, cid, " {\n}\n",
33673                          this.hdSelector, cid, " {\n}\n",
33674                          this.splitSelector, cid, " {\n}\n");
33675         }
33676         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33677     }
33678 });/*
33679  * Based on:
33680  * Ext JS Library 1.1.1
33681  * Copyright(c) 2006-2007, Ext JS, LLC.
33682  *
33683  * Originally Released Under LGPL - original licence link has changed is not relivant.
33684  *
33685  * Fork - LGPL
33686  * <script type="text/javascript">
33687  */
33688
33689 // private
33690 // This is a support class used internally by the Grid components
33691 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
33692     this.grid = grid;
33693     this.view = grid.getView();
33694     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
33695     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
33696     if(hd2){
33697         this.setHandleElId(Roo.id(hd));
33698         this.setOuterHandleElId(Roo.id(hd2));
33699     }
33700     this.scroll = false;
33701 };
33702 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
33703     maxDragWidth: 120,
33704     getDragData : function(e){
33705         var t = Roo.lib.Event.getTarget(e);
33706         var h = this.view.findHeaderCell(t);
33707         if(h){
33708             return {ddel: h.firstChild, header:h};
33709         }
33710         return false;
33711     },
33712
33713     onInitDrag : function(e){
33714         this.view.headersDisabled = true;
33715         var clone = this.dragData.ddel.cloneNode(true);
33716         clone.id = Roo.id();
33717         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
33718         this.proxy.update(clone);
33719         return true;
33720     },
33721
33722     afterValidDrop : function(){
33723         var v = this.view;
33724         setTimeout(function(){
33725             v.headersDisabled = false;
33726         }, 50);
33727     },
33728
33729     afterInvalidDrop : function(){
33730         var v = this.view;
33731         setTimeout(function(){
33732             v.headersDisabled = false;
33733         }, 50);
33734     }
33735 });
33736 /*
33737  * Based on:
33738  * Ext JS Library 1.1.1
33739  * Copyright(c) 2006-2007, Ext JS, LLC.
33740  *
33741  * Originally Released Under LGPL - original licence link has changed is not relivant.
33742  *
33743  * Fork - LGPL
33744  * <script type="text/javascript">
33745  */
33746 // private
33747 // This is a support class used internally by the Grid components
33748 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
33749     this.grid = grid;
33750     this.view = grid.getView();
33751     // split the proxies so they don't interfere with mouse events
33752     this.proxyTop = Roo.DomHelper.append(document.body, {
33753         cls:"col-move-top", html:"&#160;"
33754     }, true);
33755     this.proxyBottom = Roo.DomHelper.append(document.body, {
33756         cls:"col-move-bottom", html:"&#160;"
33757     }, true);
33758     this.proxyTop.hide = this.proxyBottom.hide = function(){
33759         this.setLeftTop(-100,-100);
33760         this.setStyle("visibility", "hidden");
33761     };
33762     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
33763     // temporarily disabled
33764     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
33765     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
33766 };
33767 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
33768     proxyOffsets : [-4, -9],
33769     fly: Roo.Element.fly,
33770
33771     getTargetFromEvent : function(e){
33772         var t = Roo.lib.Event.getTarget(e);
33773         var cindex = this.view.findCellIndex(t);
33774         if(cindex !== false){
33775             return this.view.getHeaderCell(cindex);
33776         }
33777         return null;
33778     },
33779
33780     nextVisible : function(h){
33781         var v = this.view, cm = this.grid.colModel;
33782         h = h.nextSibling;
33783         while(h){
33784             if(!cm.isHidden(v.getCellIndex(h))){
33785                 return h;
33786             }
33787             h = h.nextSibling;
33788         }
33789         return null;
33790     },
33791
33792     prevVisible : function(h){
33793         var v = this.view, cm = this.grid.colModel;
33794         h = h.prevSibling;
33795         while(h){
33796             if(!cm.isHidden(v.getCellIndex(h))){
33797                 return h;
33798             }
33799             h = h.prevSibling;
33800         }
33801         return null;
33802     },
33803
33804     positionIndicator : function(h, n, e){
33805         var x = Roo.lib.Event.getPageX(e);
33806         var r = Roo.lib.Dom.getRegion(n.firstChild);
33807         var px, pt, py = r.top + this.proxyOffsets[1];
33808         if((r.right - x) <= (r.right-r.left)/2){
33809             px = r.right+this.view.borderWidth;
33810             pt = "after";
33811         }else{
33812             px = r.left;
33813             pt = "before";
33814         }
33815         var oldIndex = this.view.getCellIndex(h);
33816         var newIndex = this.view.getCellIndex(n);
33817
33818         if(this.grid.colModel.isFixed(newIndex)){
33819             return false;
33820         }
33821
33822         var locked = this.grid.colModel.isLocked(newIndex);
33823
33824         if(pt == "after"){
33825             newIndex++;
33826         }
33827         if(oldIndex < newIndex){
33828             newIndex--;
33829         }
33830         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
33831             return false;
33832         }
33833         px +=  this.proxyOffsets[0];
33834         this.proxyTop.setLeftTop(px, py);
33835         this.proxyTop.show();
33836         if(!this.bottomOffset){
33837             this.bottomOffset = this.view.mainHd.getHeight();
33838         }
33839         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
33840         this.proxyBottom.show();
33841         return pt;
33842     },
33843
33844     onNodeEnter : function(n, dd, e, data){
33845         if(data.header != n){
33846             this.positionIndicator(data.header, n, e);
33847         }
33848     },
33849
33850     onNodeOver : function(n, dd, e, data){
33851         var result = false;
33852         if(data.header != n){
33853             result = this.positionIndicator(data.header, n, e);
33854         }
33855         if(!result){
33856             this.proxyTop.hide();
33857             this.proxyBottom.hide();
33858         }
33859         return result ? this.dropAllowed : this.dropNotAllowed;
33860     },
33861
33862     onNodeOut : function(n, dd, e, data){
33863         this.proxyTop.hide();
33864         this.proxyBottom.hide();
33865     },
33866
33867     onNodeDrop : function(n, dd, e, data){
33868         var h = data.header;
33869         if(h != n){
33870             var cm = this.grid.colModel;
33871             var x = Roo.lib.Event.getPageX(e);
33872             var r = Roo.lib.Dom.getRegion(n.firstChild);
33873             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
33874             var oldIndex = this.view.getCellIndex(h);
33875             var newIndex = this.view.getCellIndex(n);
33876             var locked = cm.isLocked(newIndex);
33877             if(pt == "after"){
33878                 newIndex++;
33879             }
33880             if(oldIndex < newIndex){
33881                 newIndex--;
33882             }
33883             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
33884                 return false;
33885             }
33886             cm.setLocked(oldIndex, locked, true);
33887             cm.moveColumn(oldIndex, newIndex);
33888             this.grid.fireEvent("columnmove", oldIndex, newIndex);
33889             return true;
33890         }
33891         return false;
33892     }
33893 });
33894 /*
33895  * Based on:
33896  * Ext JS Library 1.1.1
33897  * Copyright(c) 2006-2007, Ext JS, LLC.
33898  *
33899  * Originally Released Under LGPL - original licence link has changed is not relivant.
33900  *
33901  * Fork - LGPL
33902  * <script type="text/javascript">
33903  */
33904   
33905 /**
33906  * @class Roo.grid.GridView
33907  * @extends Roo.util.Observable
33908  *
33909  * @constructor
33910  * @param {Object} config
33911  */
33912 Roo.grid.GridView = function(config){
33913     Roo.grid.GridView.superclass.constructor.call(this);
33914     this.el = null;
33915
33916     Roo.apply(this, config);
33917 };
33918
33919 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
33920
33921     /**
33922      * Override this function to apply custom css classes to rows during rendering
33923      * @param {Record} record The record
33924      * @param {Number} index
33925      * @method getRowClass
33926      */
33927     rowClass : "x-grid-row",
33928
33929     cellClass : "x-grid-col",
33930
33931     tdClass : "x-grid-td",
33932
33933     hdClass : "x-grid-hd",
33934
33935     splitClass : "x-grid-split",
33936
33937     sortClasses : ["sort-asc", "sort-desc"],
33938
33939     enableMoveAnim : false,
33940
33941     hlColor: "C3DAF9",
33942
33943     dh : Roo.DomHelper,
33944
33945     fly : Roo.Element.fly,
33946
33947     css : Roo.util.CSS,
33948
33949     borderWidth: 1,
33950
33951     splitOffset: 3,
33952
33953     scrollIncrement : 22,
33954
33955     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
33956
33957     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
33958
33959     bind : function(ds, cm){
33960         if(this.ds){
33961             this.ds.un("load", this.onLoad, this);
33962             this.ds.un("datachanged", this.onDataChange, this);
33963             this.ds.un("add", this.onAdd, this);
33964             this.ds.un("remove", this.onRemove, this);
33965             this.ds.un("update", this.onUpdate, this);
33966             this.ds.un("clear", this.onClear, this);
33967         }
33968         if(ds){
33969             ds.on("load", this.onLoad, this);
33970             ds.on("datachanged", this.onDataChange, this);
33971             ds.on("add", this.onAdd, this);
33972             ds.on("remove", this.onRemove, this);
33973             ds.on("update", this.onUpdate, this);
33974             ds.on("clear", this.onClear, this);
33975         }
33976         this.ds = ds;
33977
33978         if(this.cm){
33979             this.cm.un("widthchange", this.onColWidthChange, this);
33980             this.cm.un("headerchange", this.onHeaderChange, this);
33981             this.cm.un("hiddenchange", this.onHiddenChange, this);
33982             this.cm.un("columnmoved", this.onColumnMove, this);
33983             this.cm.un("columnlockchange", this.onColumnLock, this);
33984         }
33985         if(cm){
33986             this.generateRules(cm);
33987             cm.on("widthchange", this.onColWidthChange, this);
33988             cm.on("headerchange", this.onHeaderChange, this);
33989             cm.on("hiddenchange", this.onHiddenChange, this);
33990             cm.on("columnmoved", this.onColumnMove, this);
33991             cm.on("columnlockchange", this.onColumnLock, this);
33992         }
33993         this.cm = cm;
33994     },
33995
33996     init: function(grid){
33997         Roo.grid.GridView.superclass.init.call(this, grid);
33998
33999         this.bind(grid.dataSource, grid.colModel);
34000
34001         grid.on("headerclick", this.handleHeaderClick, this);
34002
34003         if(grid.trackMouseOver){
34004             grid.on("mouseover", this.onRowOver, this);
34005             grid.on("mouseout", this.onRowOut, this);
34006         }
34007         grid.cancelTextSelection = function(){};
34008         this.gridId = grid.id;
34009
34010         var tpls = this.templates || {};
34011
34012         if(!tpls.master){
34013             tpls.master = new Roo.Template(
34014                '<div class="x-grid" hidefocus="true">',
34015                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
34016                   '<div class="x-grid-topbar"></div>',
34017                   '<div class="x-grid-scroller"><div></div></div>',
34018                   '<div class="x-grid-locked">',
34019                       '<div class="x-grid-header">{lockedHeader}</div>',
34020                       '<div class="x-grid-body">{lockedBody}</div>',
34021                   "</div>",
34022                   '<div class="x-grid-viewport">',
34023                       '<div class="x-grid-header">{header}</div>',
34024                       '<div class="x-grid-body">{body}</div>',
34025                   "</div>",
34026                   '<div class="x-grid-bottombar"></div>',
34027                  
34028                   '<div class="x-grid-resize-proxy">&#160;</div>',
34029                "</div>"
34030             );
34031             tpls.master.disableformats = true;
34032         }
34033
34034         if(!tpls.header){
34035             tpls.header = new Roo.Template(
34036                '<table border="0" cellspacing="0" cellpadding="0">',
34037                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
34038                "</table>{splits}"
34039             );
34040             tpls.header.disableformats = true;
34041         }
34042         tpls.header.compile();
34043
34044         if(!tpls.hcell){
34045             tpls.hcell = new Roo.Template(
34046                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
34047                 '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
34048                 "</div></td>"
34049              );
34050              tpls.hcell.disableFormats = true;
34051         }
34052         tpls.hcell.compile();
34053
34054         if(!tpls.hsplit){
34055             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
34056             tpls.hsplit.disableFormats = true;
34057         }
34058         tpls.hsplit.compile();
34059
34060         if(!tpls.body){
34061             tpls.body = new Roo.Template(
34062                '<table border="0" cellspacing="0" cellpadding="0">',
34063                "<tbody>{rows}</tbody>",
34064                "</table>"
34065             );
34066             tpls.body.disableFormats = true;
34067         }
34068         tpls.body.compile();
34069
34070         if(!tpls.row){
34071             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
34072             tpls.row.disableFormats = true;
34073         }
34074         tpls.row.compile();
34075
34076         if(!tpls.cell){
34077             tpls.cell = new Roo.Template(
34078                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
34079                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
34080                 "</td>"
34081             );
34082             tpls.cell.disableFormats = true;
34083         }
34084         tpls.cell.compile();
34085
34086         this.templates = tpls;
34087     },
34088
34089     // remap these for backwards compat
34090     onColWidthChange : function(){
34091         this.updateColumns.apply(this, arguments);
34092     },
34093     onHeaderChange : function(){
34094         this.updateHeaders.apply(this, arguments);
34095     }, 
34096     onHiddenChange : function(){
34097         this.handleHiddenChange.apply(this, arguments);
34098     },
34099     onColumnMove : function(){
34100         this.handleColumnMove.apply(this, arguments);
34101     },
34102     onColumnLock : function(){
34103         this.handleLockChange.apply(this, arguments);
34104     },
34105
34106     onDataChange : function(){
34107         this.refresh();
34108         this.updateHeaderSortState();
34109     },
34110
34111     onClear : function(){
34112         this.refresh();
34113     },
34114
34115     onUpdate : function(ds, record){
34116         this.refreshRow(record);
34117     },
34118
34119     refreshRow : function(record){
34120         var ds = this.ds, index;
34121         if(typeof record == 'number'){
34122             index = record;
34123             record = ds.getAt(index);
34124         }else{
34125             index = ds.indexOf(record);
34126         }
34127         this.insertRows(ds, index, index, true);
34128         this.onRemove(ds, record, index+1, true);
34129         this.syncRowHeights(index, index);
34130         this.layout();
34131         this.fireEvent("rowupdated", this, index, record);
34132     },
34133
34134     onAdd : function(ds, records, index){
34135         this.insertRows(ds, index, index + (records.length-1));
34136     },
34137
34138     onRemove : function(ds, record, index, isUpdate){
34139         if(isUpdate !== true){
34140             this.fireEvent("beforerowremoved", this, index, record);
34141         }
34142         var bt = this.getBodyTable(), lt = this.getLockedTable();
34143         if(bt.rows[index]){
34144             bt.firstChild.removeChild(bt.rows[index]);
34145         }
34146         if(lt.rows[index]){
34147             lt.firstChild.removeChild(lt.rows[index]);
34148         }
34149         if(isUpdate !== true){
34150             this.stripeRows(index);
34151             this.syncRowHeights(index, index);
34152             this.layout();
34153             this.fireEvent("rowremoved", this, index, record);
34154         }
34155     },
34156
34157     onLoad : function(){
34158         this.scrollToTop();
34159     },
34160
34161     /**
34162      * Scrolls the grid to the top
34163      */
34164     scrollToTop : function(){
34165         if(this.scroller){
34166             this.scroller.dom.scrollTop = 0;
34167             this.syncScroll();
34168         }
34169     },
34170
34171     /**
34172      * Gets a panel in the header of the grid that can be used for toolbars etc.
34173      * After modifying the contents of this panel a call to grid.autoSize() may be
34174      * required to register any changes in size.
34175      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
34176      * @return Roo.Element
34177      */
34178     getHeaderPanel : function(doShow){
34179         if(doShow){
34180             this.headerPanel.show();
34181         }
34182         return this.headerPanel;
34183     },
34184
34185     /**
34186      * Gets a panel in the footer of the grid that can be used for toolbars etc.
34187      * After modifying the contents of this panel a call to grid.autoSize() may be
34188      * required to register any changes in size.
34189      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
34190      * @return Roo.Element
34191      */
34192     getFooterPanel : function(doShow){
34193         if(doShow){
34194             this.footerPanel.show();
34195         }
34196         return this.footerPanel;
34197     },
34198
34199     initElements : function(){
34200         var E = Roo.Element;
34201         var el = this.grid.getGridEl().dom.firstChild;
34202         var cs = el.childNodes;
34203
34204         this.el = new E(el);
34205         
34206          this.focusEl = new E(el.firstChild);
34207         this.focusEl.swallowEvent("click", true);
34208         
34209         this.headerPanel = new E(cs[1]);
34210         this.headerPanel.enableDisplayMode("block");
34211
34212         this.scroller = new E(cs[2]);
34213         this.scrollSizer = new E(this.scroller.dom.firstChild);
34214
34215         this.lockedWrap = new E(cs[3]);
34216         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
34217         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
34218
34219         this.mainWrap = new E(cs[4]);
34220         this.mainHd = new E(this.mainWrap.dom.firstChild);
34221         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
34222
34223         this.footerPanel = new E(cs[5]);
34224         this.footerPanel.enableDisplayMode("block");
34225
34226         this.resizeProxy = new E(cs[6]);
34227
34228         this.headerSelector = String.format(
34229            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
34230            this.lockedHd.id, this.mainHd.id
34231         );
34232
34233         this.splitterSelector = String.format(
34234            '#{0} div.x-grid-split, #{1} div.x-grid-split',
34235            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
34236         );
34237     },
34238     idToCssName : function(s)
34239     {
34240         return s.replace(/[^a-z0-9]+/ig, '-');
34241     },
34242
34243     getHeaderCell : function(index){
34244         return Roo.DomQuery.select(this.headerSelector)[index];
34245     },
34246
34247     getHeaderCellMeasure : function(index){
34248         return this.getHeaderCell(index).firstChild;
34249     },
34250
34251     getHeaderCellText : function(index){
34252         return this.getHeaderCell(index).firstChild.firstChild;
34253     },
34254
34255     getLockedTable : function(){
34256         return this.lockedBody.dom.firstChild;
34257     },
34258
34259     getBodyTable : function(){
34260         return this.mainBody.dom.firstChild;
34261     },
34262
34263     getLockedRow : function(index){
34264         return this.getLockedTable().rows[index];
34265     },
34266
34267     getRow : function(index){
34268         return this.getBodyTable().rows[index];
34269     },
34270
34271     getRowComposite : function(index){
34272         if(!this.rowEl){
34273             this.rowEl = new Roo.CompositeElementLite();
34274         }
34275         var els = [], lrow, mrow;
34276         if(lrow = this.getLockedRow(index)){
34277             els.push(lrow);
34278         }
34279         if(mrow = this.getRow(index)){
34280             els.push(mrow);
34281         }
34282         this.rowEl.elements = els;
34283         return this.rowEl;
34284     },
34285     /**
34286      * Gets the 'td' of the cell
34287      * 
34288      * @param {Integer} rowIndex row to select
34289      * @param {Integer} colIndex column to select
34290      * 
34291      * @return {Object} 
34292      */
34293     getCell : function(rowIndex, colIndex){
34294         var locked = this.cm.getLockedCount();
34295         var source;
34296         if(colIndex < locked){
34297             source = this.lockedBody.dom.firstChild;
34298         }else{
34299             source = this.mainBody.dom.firstChild;
34300             colIndex -= locked;
34301         }
34302         return source.rows[rowIndex].childNodes[colIndex];
34303     },
34304
34305     getCellText : function(rowIndex, colIndex){
34306         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
34307     },
34308
34309     getCellBox : function(cell){
34310         var b = this.fly(cell).getBox();
34311         if(Roo.isOpera){ // opera fails to report the Y
34312             b.y = cell.offsetTop + this.mainBody.getY();
34313         }
34314         return b;
34315     },
34316
34317     getCellIndex : function(cell){
34318         var id = String(cell.className).match(this.cellRE);
34319         if(id){
34320             return parseInt(id[1], 10);
34321         }
34322         return 0;
34323     },
34324
34325     findHeaderIndex : function(n){
34326         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
34327         return r ? this.getCellIndex(r) : false;
34328     },
34329
34330     findHeaderCell : function(n){
34331         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
34332         return r ? r : false;
34333     },
34334
34335     findRowIndex : function(n){
34336         if(!n){
34337             return false;
34338         }
34339         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
34340         return r ? r.rowIndex : false;
34341     },
34342
34343     findCellIndex : function(node){
34344         var stop = this.el.dom;
34345         while(node && node != stop){
34346             if(this.findRE.test(node.className)){
34347                 return this.getCellIndex(node);
34348             }
34349             node = node.parentNode;
34350         }
34351         return false;
34352     },
34353
34354     getColumnId : function(index){
34355         return this.cm.getColumnId(index);
34356     },
34357
34358     getSplitters : function()
34359     {
34360         if(this.splitterSelector){
34361            return Roo.DomQuery.select(this.splitterSelector);
34362         }else{
34363             return null;
34364       }
34365     },
34366
34367     getSplitter : function(index){
34368         return this.getSplitters()[index];
34369     },
34370
34371     onRowOver : function(e, t){
34372         var row;
34373         if((row = this.findRowIndex(t)) !== false){
34374             this.getRowComposite(row).addClass("x-grid-row-over");
34375         }
34376     },
34377
34378     onRowOut : function(e, t){
34379         var row;
34380         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
34381             this.getRowComposite(row).removeClass("x-grid-row-over");
34382         }
34383     },
34384
34385     renderHeaders : function(){
34386         var cm = this.cm;
34387         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
34388         var cb = [], lb = [], sb = [], lsb = [], p = {};
34389         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34390             p.cellId = "x-grid-hd-0-" + i;
34391             p.splitId = "x-grid-csplit-0-" + i;
34392             p.id = cm.getColumnId(i);
34393             p.title = cm.getColumnTooltip(i) || "";
34394             p.value = cm.getColumnHeader(i) || "";
34395             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
34396             if(!cm.isLocked(i)){
34397                 cb[cb.length] = ct.apply(p);
34398                 sb[sb.length] = st.apply(p);
34399             }else{
34400                 lb[lb.length] = ct.apply(p);
34401                 lsb[lsb.length] = st.apply(p);
34402             }
34403         }
34404         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
34405                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
34406     },
34407
34408     updateHeaders : function(){
34409         var html = this.renderHeaders();
34410         this.lockedHd.update(html[0]);
34411         this.mainHd.update(html[1]);
34412     },
34413
34414     /**
34415      * Focuses the specified row.
34416      * @param {Number} row The row index
34417      */
34418     focusRow : function(row)
34419     {
34420         //Roo.log('GridView.focusRow');
34421         var x = this.scroller.dom.scrollLeft;
34422         this.focusCell(row, 0, false);
34423         this.scroller.dom.scrollLeft = x;
34424     },
34425
34426     /**
34427      * Focuses the specified cell.
34428      * @param {Number} row The row index
34429      * @param {Number} col The column index
34430      * @param {Boolean} hscroll false to disable horizontal scrolling
34431      */
34432     focusCell : function(row, col, hscroll)
34433     {
34434         //Roo.log('GridView.focusCell');
34435         var el = this.ensureVisible(row, col, hscroll);
34436         this.focusEl.alignTo(el, "tl-tl");
34437         if(Roo.isGecko){
34438             this.focusEl.focus();
34439         }else{
34440             this.focusEl.focus.defer(1, this.focusEl);
34441         }
34442     },
34443
34444     /**
34445      * Scrolls the specified cell into view
34446      * @param {Number} row The row index
34447      * @param {Number} col The column index
34448      * @param {Boolean} hscroll false to disable horizontal scrolling
34449      */
34450     ensureVisible : function(row, col, hscroll)
34451     {
34452         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
34453         //return null; //disable for testing.
34454         if(typeof row != "number"){
34455             row = row.rowIndex;
34456         }
34457         if(row < 0 && row >= this.ds.getCount()){
34458             return  null;
34459         }
34460         col = (col !== undefined ? col : 0);
34461         var cm = this.grid.colModel;
34462         while(cm.isHidden(col)){
34463             col++;
34464         }
34465
34466         var el = this.getCell(row, col);
34467         if(!el){
34468             return null;
34469         }
34470         var c = this.scroller.dom;
34471
34472         var ctop = parseInt(el.offsetTop, 10);
34473         var cleft = parseInt(el.offsetLeft, 10);
34474         var cbot = ctop + el.offsetHeight;
34475         var cright = cleft + el.offsetWidth;
34476         
34477         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
34478         var stop = parseInt(c.scrollTop, 10);
34479         var sleft = parseInt(c.scrollLeft, 10);
34480         var sbot = stop + ch;
34481         var sright = sleft + c.clientWidth;
34482         /*
34483         Roo.log('GridView.ensureVisible:' +
34484                 ' ctop:' + ctop +
34485                 ' c.clientHeight:' + c.clientHeight +
34486                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
34487                 ' stop:' + stop +
34488                 ' cbot:' + cbot +
34489                 ' sbot:' + sbot +
34490                 ' ch:' + ch  
34491                 );
34492         */
34493         if(ctop < stop){
34494              c.scrollTop = ctop;
34495             //Roo.log("set scrolltop to ctop DISABLE?");
34496         }else if(cbot > sbot){
34497             //Roo.log("set scrolltop to cbot-ch");
34498             c.scrollTop = cbot-ch;
34499         }
34500         
34501         if(hscroll !== false){
34502             if(cleft < sleft){
34503                 c.scrollLeft = cleft;
34504             }else if(cright > sright){
34505                 c.scrollLeft = cright-c.clientWidth;
34506             }
34507         }
34508          
34509         return el;
34510     },
34511
34512     updateColumns : function(){
34513         this.grid.stopEditing();
34514         var cm = this.grid.colModel, colIds = this.getColumnIds();
34515         //var totalWidth = cm.getTotalWidth();
34516         var pos = 0;
34517         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34518             //if(cm.isHidden(i)) continue;
34519             var w = cm.getColumnWidth(i);
34520             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
34521             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
34522         }
34523         this.updateSplitters();
34524     },
34525
34526     generateRules : function(cm){
34527         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
34528         Roo.util.CSS.removeStyleSheet(rulesId);
34529         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34530             var cid = cm.getColumnId(i);
34531             var align = '';
34532             if(cm.config[i].align){
34533                 align = 'text-align:'+cm.config[i].align+';';
34534             }
34535             var hidden = '';
34536             if(cm.isHidden(i)){
34537                 hidden = 'display:none;';
34538             }
34539             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
34540             ruleBuf.push(
34541                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
34542                     this.hdSelector, cid, " {\n", align, width, "}\n",
34543                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
34544                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
34545         }
34546         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
34547     },
34548
34549     updateSplitters : function(){
34550         var cm = this.cm, s = this.getSplitters();
34551         if(s){ // splitters not created yet
34552             var pos = 0, locked = true;
34553             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34554                 if(cm.isHidden(i)) continue;
34555                 var w = cm.getColumnWidth(i); // make sure it's a number
34556                 if(!cm.isLocked(i) && locked){
34557                     pos = 0;
34558                     locked = false;
34559                 }
34560                 pos += w;
34561                 s[i].style.left = (pos-this.splitOffset) + "px";
34562             }
34563         }
34564     },
34565
34566     handleHiddenChange : function(colModel, colIndex, hidden){
34567         if(hidden){
34568             this.hideColumn(colIndex);
34569         }else{
34570             this.unhideColumn(colIndex);
34571         }
34572     },
34573
34574     hideColumn : function(colIndex){
34575         var cid = this.getColumnId(colIndex);
34576         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
34577         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
34578         if(Roo.isSafari){
34579             this.updateHeaders();
34580         }
34581         this.updateSplitters();
34582         this.layout();
34583     },
34584
34585     unhideColumn : function(colIndex){
34586         var cid = this.getColumnId(colIndex);
34587         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
34588         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
34589
34590         if(Roo.isSafari){
34591             this.updateHeaders();
34592         }
34593         this.updateSplitters();
34594         this.layout();
34595     },
34596
34597     insertRows : function(dm, firstRow, lastRow, isUpdate){
34598         if(firstRow == 0 && lastRow == dm.getCount()-1){
34599             this.refresh();
34600         }else{
34601             if(!isUpdate){
34602                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
34603             }
34604             var s = this.getScrollState();
34605             var markup = this.renderRows(firstRow, lastRow);
34606             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
34607             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
34608             this.restoreScroll(s);
34609             if(!isUpdate){
34610                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
34611                 this.syncRowHeights(firstRow, lastRow);
34612                 this.stripeRows(firstRow);
34613                 this.layout();
34614             }
34615         }
34616     },
34617
34618     bufferRows : function(markup, target, index){
34619         var before = null, trows = target.rows, tbody = target.tBodies[0];
34620         if(index < trows.length){
34621             before = trows[index];
34622         }
34623         var b = document.createElement("div");
34624         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
34625         var rows = b.firstChild.rows;
34626         for(var i = 0, len = rows.length; i < len; i++){
34627             if(before){
34628                 tbody.insertBefore(rows[0], before);
34629             }else{
34630                 tbody.appendChild(rows[0]);
34631             }
34632         }
34633         b.innerHTML = "";
34634         b = null;
34635     },
34636
34637     deleteRows : function(dm, firstRow, lastRow){
34638         if(dm.getRowCount()<1){
34639             this.fireEvent("beforerefresh", this);
34640             this.mainBody.update("");
34641             this.lockedBody.update("");
34642             this.fireEvent("refresh", this);
34643         }else{
34644             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
34645             var bt = this.getBodyTable();
34646             var tbody = bt.firstChild;
34647             var rows = bt.rows;
34648             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
34649                 tbody.removeChild(rows[firstRow]);
34650             }
34651             this.stripeRows(firstRow);
34652             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
34653         }
34654     },
34655
34656     updateRows : function(dataSource, firstRow, lastRow){
34657         var s = this.getScrollState();
34658         this.refresh();
34659         this.restoreScroll(s);
34660     },
34661
34662     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
34663         if(!noRefresh){
34664            this.refresh();
34665         }
34666         this.updateHeaderSortState();
34667     },
34668
34669     getScrollState : function(){
34670         
34671         var sb = this.scroller.dom;
34672         return {left: sb.scrollLeft, top: sb.scrollTop};
34673     },
34674
34675     stripeRows : function(startRow){
34676         if(!this.grid.stripeRows || this.ds.getCount() < 1){
34677             return;
34678         }
34679         startRow = startRow || 0;
34680         var rows = this.getBodyTable().rows;
34681         var lrows = this.getLockedTable().rows;
34682         var cls = ' x-grid-row-alt ';
34683         for(var i = startRow, len = rows.length; i < len; i++){
34684             var row = rows[i], lrow = lrows[i];
34685             var isAlt = ((i+1) % 2 == 0);
34686             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
34687             if(isAlt == hasAlt){
34688                 continue;
34689             }
34690             if(isAlt){
34691                 row.className += " x-grid-row-alt";
34692             }else{
34693                 row.className = row.className.replace("x-grid-row-alt", "");
34694             }
34695             if(lrow){
34696                 lrow.className = row.className;
34697             }
34698         }
34699     },
34700
34701     restoreScroll : function(state){
34702         //Roo.log('GridView.restoreScroll');
34703         var sb = this.scroller.dom;
34704         sb.scrollLeft = state.left;
34705         sb.scrollTop = state.top;
34706         this.syncScroll();
34707     },
34708
34709     syncScroll : function(){
34710         //Roo.log('GridView.syncScroll');
34711         var sb = this.scroller.dom;
34712         var sh = this.mainHd.dom;
34713         var bs = this.mainBody.dom;
34714         var lv = this.lockedBody.dom;
34715         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
34716         lv.scrollTop = bs.scrollTop = sb.scrollTop;
34717     },
34718
34719     handleScroll : function(e){
34720         this.syncScroll();
34721         var sb = this.scroller.dom;
34722         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
34723         e.stopEvent();
34724     },
34725
34726     handleWheel : function(e){
34727         var d = e.getWheelDelta();
34728         this.scroller.dom.scrollTop -= d*22;
34729         // set this here to prevent jumpy scrolling on large tables
34730         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
34731         e.stopEvent();
34732     },
34733
34734     renderRows : function(startRow, endRow){
34735         // pull in all the crap needed to render rows
34736         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
34737         var colCount = cm.getColumnCount();
34738
34739         if(ds.getCount() < 1){
34740             return ["", ""];
34741         }
34742
34743         // build a map for all the columns
34744         var cs = [];
34745         for(var i = 0; i < colCount; i++){
34746             var name = cm.getDataIndex(i);
34747             cs[i] = {
34748                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
34749                 renderer : cm.getRenderer(i),
34750                 id : cm.getColumnId(i),
34751                 locked : cm.isLocked(i)
34752             };
34753         }
34754
34755         startRow = startRow || 0;
34756         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
34757
34758         // records to render
34759         var rs = ds.getRange(startRow, endRow);
34760
34761         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
34762     },
34763
34764     // As much as I hate to duplicate code, this was branched because FireFox really hates
34765     // [].join("") on strings. The performance difference was substantial enough to
34766     // branch this function
34767     doRender : Roo.isGecko ?
34768             function(cs, rs, ds, startRow, colCount, stripe){
34769                 var ts = this.templates, ct = ts.cell, rt = ts.row;
34770                 // buffers
34771                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
34772                 
34773                 var hasListener = this.grid.hasListener('rowclass');
34774                 var rowcfg = {};
34775                 for(var j = 0, len = rs.length; j < len; j++){
34776                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
34777                     for(var i = 0; i < colCount; i++){
34778                         c = cs[i];
34779                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
34780                         p.id = c.id;
34781                         p.css = p.attr = "";
34782                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
34783                         if(p.value == undefined || p.value === "") p.value = "&#160;";
34784                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
34785                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
34786                         }
34787                         var markup = ct.apply(p);
34788                         if(!c.locked){
34789                             cb+= markup;
34790                         }else{
34791                             lcb+= markup;
34792                         }
34793                     }
34794                     var alt = [];
34795                     if(stripe && ((rowIndex+1) % 2 == 0)){
34796                         alt.push("x-grid-row-alt")
34797                     }
34798                     if(r.dirty){
34799                         alt.push(  " x-grid-dirty-row");
34800                     }
34801                     rp.cells = lcb;
34802                     if(this.getRowClass){
34803                         alt.push(this.getRowClass(r, rowIndex));
34804                     }
34805                     if (hasListener) {
34806                         rowcfg = {
34807                              
34808                             record: r,
34809                             rowIndex : rowIndex,
34810                             rowClass : ''
34811                         }
34812                         this.grid.fireEvent('rowclass', this, rowcfg);
34813                         alt.push(rowcfg.rowClass);
34814                     }
34815                     rp.alt = alt.join(" ");
34816                     lbuf+= rt.apply(rp);
34817                     rp.cells = cb;
34818                     buf+=  rt.apply(rp);
34819                 }
34820                 return [lbuf, buf];
34821             } :
34822             function(cs, rs, ds, startRow, colCount, stripe){
34823                 var ts = this.templates, ct = ts.cell, rt = ts.row;
34824                 // buffers
34825                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
34826                 var hasListener = this.grid.hasListener('rowclass');
34827                 var rowcfg = {};
34828                 for(var j = 0, len = rs.length; j < len; j++){
34829                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
34830                     for(var i = 0; i < colCount; i++){
34831                         c = cs[i];
34832                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
34833                         p.id = c.id;
34834                         p.css = p.attr = "";
34835                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
34836                         if(p.value == undefined || p.value === "") p.value = "&#160;";
34837                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
34838                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
34839                         }
34840                         var markup = ct.apply(p);
34841                         if(!c.locked){
34842                             cb[cb.length] = markup;
34843                         }else{
34844                             lcb[lcb.length] = markup;
34845                         }
34846                     }
34847                     var alt = [];
34848                     if(stripe && ((rowIndex+1) % 2 == 0)){
34849                         alt.push( "x-grid-row-alt");
34850                     }
34851                     if(r.dirty){
34852                         alt.push(" x-grid-dirty-row");
34853                     }
34854                     rp.cells = lcb;
34855                     if(this.getRowClass){
34856                         alt.push( this.getRowClass(r, rowIndex));
34857                     }
34858                     if (hasListener) {
34859                         rowcfg = {
34860                              
34861                             record: r,
34862                             rowIndex : rowIndex,
34863                             rowClass : ''
34864                         }
34865                         this.grid.fireEvent('rowclass', this, rowcfg);
34866                         alt.push(rowcfg.rowClass);
34867                     }
34868                     rp.alt = alt.join(" ");
34869                     rp.cells = lcb.join("");
34870                     lbuf[lbuf.length] = rt.apply(rp);
34871                     rp.cells = cb.join("");
34872                     buf[buf.length] =  rt.apply(rp);
34873                 }
34874                 return [lbuf.join(""), buf.join("")];
34875             },
34876
34877     renderBody : function(){
34878         var markup = this.renderRows();
34879         var bt = this.templates.body;
34880         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
34881     },
34882
34883     /**
34884      * Refreshes the grid
34885      * @param {Boolean} headersToo
34886      */
34887     refresh : function(headersToo){
34888         this.fireEvent("beforerefresh", this);
34889         this.grid.stopEditing();
34890         var result = this.renderBody();
34891         this.lockedBody.update(result[0]);
34892         this.mainBody.update(result[1]);
34893         if(headersToo === true){
34894             this.updateHeaders();
34895             this.updateColumns();
34896             this.updateSplitters();
34897             this.updateHeaderSortState();
34898         }
34899         this.syncRowHeights();
34900         this.layout();
34901         this.fireEvent("refresh", this);
34902     },
34903
34904     handleColumnMove : function(cm, oldIndex, newIndex){
34905         this.indexMap = null;
34906         var s = this.getScrollState();
34907         this.refresh(true);
34908         this.restoreScroll(s);
34909         this.afterMove(newIndex);
34910     },
34911
34912     afterMove : function(colIndex){
34913         if(this.enableMoveAnim && Roo.enableFx){
34914             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
34915         }
34916         // if multisort - fix sortOrder, and reload..
34917         if (this.grid.dataSource.multiSort) {
34918             // the we can call sort again..
34919             var dm = this.grid.dataSource;
34920             var cm = this.grid.colModel;
34921             var so = [];
34922             for(var i = 0; i < cm.config.length; i++ ) {
34923                 
34924                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
34925                     continue; // dont' bother, it's not in sort list or being set.
34926                 }
34927                 
34928                 so.push(cm.config[i].dataIndex);
34929             };
34930             dm.sortOrder = so;
34931             dm.load(dm.lastOptions);
34932             
34933             
34934         }
34935         
34936     },
34937
34938     updateCell : function(dm, rowIndex, dataIndex){
34939         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
34940         if(typeof colIndex == "undefined"){ // not present in grid
34941             return;
34942         }
34943         var cm = this.grid.colModel;
34944         var cell = this.getCell(rowIndex, colIndex);
34945         var cellText = this.getCellText(rowIndex, colIndex);
34946
34947         var p = {
34948             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
34949             id : cm.getColumnId(colIndex),
34950             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
34951         };
34952         var renderer = cm.getRenderer(colIndex);
34953         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
34954         if(typeof val == "undefined" || val === "") val = "&#160;";
34955         cellText.innerHTML = val;
34956         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
34957         this.syncRowHeights(rowIndex, rowIndex);
34958     },
34959
34960     calcColumnWidth : function(colIndex, maxRowsToMeasure){
34961         var maxWidth = 0;
34962         if(this.grid.autoSizeHeaders){
34963             var h = this.getHeaderCellMeasure(colIndex);
34964             maxWidth = Math.max(maxWidth, h.scrollWidth);
34965         }
34966         var tb, index;
34967         if(this.cm.isLocked(colIndex)){
34968             tb = this.getLockedTable();
34969             index = colIndex;
34970         }else{
34971             tb = this.getBodyTable();
34972             index = colIndex - this.cm.getLockedCount();
34973         }
34974         if(tb && tb.rows){
34975             var rows = tb.rows;
34976             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
34977             for(var i = 0; i < stopIndex; i++){
34978                 var cell = rows[i].childNodes[index].firstChild;
34979                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
34980             }
34981         }
34982         return maxWidth + /*margin for error in IE*/ 5;
34983     },
34984     /**
34985      * Autofit a column to its content.
34986      * @param {Number} colIndex
34987      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
34988      */
34989      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
34990          if(this.cm.isHidden(colIndex)){
34991              return; // can't calc a hidden column
34992          }
34993         if(forceMinSize){
34994             var cid = this.cm.getColumnId(colIndex);
34995             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
34996            if(this.grid.autoSizeHeaders){
34997                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
34998            }
34999         }
35000         var newWidth = this.calcColumnWidth(colIndex);
35001         this.cm.setColumnWidth(colIndex,
35002             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
35003         if(!suppressEvent){
35004             this.grid.fireEvent("columnresize", colIndex, newWidth);
35005         }
35006     },
35007
35008     /**
35009      * Autofits all columns to their content and then expands to fit any extra space in the grid
35010      */
35011      autoSizeColumns : function(){
35012         var cm = this.grid.colModel;
35013         var colCount = cm.getColumnCount();
35014         for(var i = 0; i < colCount; i++){
35015             this.autoSizeColumn(i, true, true);
35016         }
35017         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
35018             this.fitColumns();
35019         }else{
35020             this.updateColumns();
35021             this.layout();
35022         }
35023     },
35024
35025     /**
35026      * Autofits all columns to the grid's width proportionate with their current size
35027      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
35028      */
35029     fitColumns : function(reserveScrollSpace){
35030         var cm = this.grid.colModel;
35031         var colCount = cm.getColumnCount();
35032         var cols = [];
35033         var width = 0;
35034         var i, w;
35035         for (i = 0; i < colCount; i++){
35036             if(!cm.isHidden(i) && !cm.isFixed(i)){
35037                 w = cm.getColumnWidth(i);
35038                 cols.push(i);
35039                 cols.push(w);
35040                 width += w;
35041             }
35042         }
35043         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
35044         if(reserveScrollSpace){
35045             avail -= 17;
35046         }
35047         var frac = (avail - cm.getTotalWidth())/width;
35048         while (cols.length){
35049             w = cols.pop();
35050             i = cols.pop();
35051             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
35052         }
35053         this.updateColumns();
35054         this.layout();
35055     },
35056
35057     onRowSelect : function(rowIndex){
35058         var row = this.getRowComposite(rowIndex);
35059         row.addClass("x-grid-row-selected");
35060     },
35061
35062     onRowDeselect : function(rowIndex){
35063         var row = this.getRowComposite(rowIndex);
35064         row.removeClass("x-grid-row-selected");
35065     },
35066
35067     onCellSelect : function(row, col){
35068         var cell = this.getCell(row, col);
35069         if(cell){
35070             Roo.fly(cell).addClass("x-grid-cell-selected");
35071         }
35072     },
35073
35074     onCellDeselect : function(row, col){
35075         var cell = this.getCell(row, col);
35076         if(cell){
35077             Roo.fly(cell).removeClass("x-grid-cell-selected");
35078         }
35079     },
35080
35081     updateHeaderSortState : function(){
35082         
35083         // sort state can be single { field: xxx, direction : yyy}
35084         // or   { xxx=>ASC , yyy : DESC ..... }
35085         
35086         var mstate = {};
35087         if (!this.ds.multiSort) { 
35088             var state = this.ds.getSortState();
35089             if(!state){
35090                 return;
35091             }
35092             mstate[state.field] = state.direction;
35093             // FIXME... - this is not used here.. but might be elsewhere..
35094             this.sortState = state;
35095             
35096         } else {
35097             mstate = this.ds.sortToggle;
35098         }
35099         //remove existing sort classes..
35100         
35101         var sc = this.sortClasses;
35102         var hds = this.el.select(this.headerSelector).removeClass(sc);
35103         
35104         for(var f in mstate) {
35105         
35106             var sortColumn = this.cm.findColumnIndex(f);
35107             
35108             if(sortColumn != -1){
35109                 var sortDir = mstate[f];        
35110                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
35111             }
35112         }
35113         
35114          
35115         
35116     },
35117
35118
35119     handleHeaderClick : function(g, index){
35120         if(this.headersDisabled){
35121             return;
35122         }
35123         var dm = g.dataSource, cm = g.colModel;
35124         if(!cm.isSortable(index)){
35125             return;
35126         }
35127         g.stopEditing();
35128         
35129         if (dm.multiSort) {
35130             // update the sortOrder
35131             var so = [];
35132             for(var i = 0; i < cm.config.length; i++ ) {
35133                 
35134                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
35135                     continue; // dont' bother, it's not in sort list or being set.
35136                 }
35137                 
35138                 so.push(cm.config[i].dataIndex);
35139             };
35140             dm.sortOrder = so;
35141         }
35142         
35143         
35144         dm.sort(cm.getDataIndex(index));
35145     },
35146
35147
35148     destroy : function(){
35149         if(this.colMenu){
35150             this.colMenu.removeAll();
35151             Roo.menu.MenuMgr.unregister(this.colMenu);
35152             this.colMenu.getEl().remove();
35153             delete this.colMenu;
35154         }
35155         if(this.hmenu){
35156             this.hmenu.removeAll();
35157             Roo.menu.MenuMgr.unregister(this.hmenu);
35158             this.hmenu.getEl().remove();
35159             delete this.hmenu;
35160         }
35161         if(this.grid.enableColumnMove){
35162             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
35163             if(dds){
35164                 for(var dd in dds){
35165                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
35166                         var elid = dds[dd].dragElId;
35167                         dds[dd].unreg();
35168                         Roo.get(elid).remove();
35169                     } else if(dds[dd].config.isTarget){
35170                         dds[dd].proxyTop.remove();
35171                         dds[dd].proxyBottom.remove();
35172                         dds[dd].unreg();
35173                     }
35174                     if(Roo.dd.DDM.locationCache[dd]){
35175                         delete Roo.dd.DDM.locationCache[dd];
35176                     }
35177                 }
35178                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
35179             }
35180         }
35181         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
35182         this.bind(null, null);
35183         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
35184     },
35185
35186     handleLockChange : function(){
35187         this.refresh(true);
35188     },
35189
35190     onDenyColumnLock : function(){
35191
35192     },
35193
35194     onDenyColumnHide : function(){
35195
35196     },
35197
35198     handleHdMenuClick : function(item){
35199         var index = this.hdCtxIndex;
35200         var cm = this.cm, ds = this.ds;
35201         switch(item.id){
35202             case "asc":
35203                 ds.sort(cm.getDataIndex(index), "ASC");
35204                 break;
35205             case "desc":
35206                 ds.sort(cm.getDataIndex(index), "DESC");
35207                 break;
35208             case "lock":
35209                 var lc = cm.getLockedCount();
35210                 if(cm.getColumnCount(true) <= lc+1){
35211                     this.onDenyColumnLock();
35212                     return;
35213                 }
35214                 if(lc != index){
35215                     cm.setLocked(index, true, true);
35216                     cm.moveColumn(index, lc);
35217                     this.grid.fireEvent("columnmove", index, lc);
35218                 }else{
35219                     cm.setLocked(index, true);
35220                 }
35221             break;
35222             case "unlock":
35223                 var lc = cm.getLockedCount();
35224                 if((lc-1) != index){
35225                     cm.setLocked(index, false, true);
35226                     cm.moveColumn(index, lc-1);
35227                     this.grid.fireEvent("columnmove", index, lc-1);
35228                 }else{
35229                     cm.setLocked(index, false);
35230                 }
35231             break;
35232             default:
35233                 index = cm.getIndexById(item.id.substr(4));
35234                 if(index != -1){
35235                     if(item.checked && cm.getColumnCount(true) <= 1){
35236                         this.onDenyColumnHide();
35237                         return false;
35238                     }
35239                     cm.setHidden(index, item.checked);
35240                 }
35241         }
35242         return true;
35243     },
35244
35245     beforeColMenuShow : function(){
35246         var cm = this.cm,  colCount = cm.getColumnCount();
35247         this.colMenu.removeAll();
35248         for(var i = 0; i < colCount; i++){
35249             this.colMenu.add(new Roo.menu.CheckItem({
35250                 id: "col-"+cm.getColumnId(i),
35251                 text: cm.getColumnHeader(i),
35252                 checked: !cm.isHidden(i),
35253                 hideOnClick:false
35254             }));
35255         }
35256     },
35257
35258     handleHdCtx : function(g, index, e){
35259         e.stopEvent();
35260         var hd = this.getHeaderCell(index);
35261         this.hdCtxIndex = index;
35262         var ms = this.hmenu.items, cm = this.cm;
35263         ms.get("asc").setDisabled(!cm.isSortable(index));
35264         ms.get("desc").setDisabled(!cm.isSortable(index));
35265         if(this.grid.enableColLock !== false){
35266             ms.get("lock").setDisabled(cm.isLocked(index));
35267             ms.get("unlock").setDisabled(!cm.isLocked(index));
35268         }
35269         this.hmenu.show(hd, "tl-bl");
35270     },
35271
35272     handleHdOver : function(e){
35273         var hd = this.findHeaderCell(e.getTarget());
35274         if(hd && !this.headersDisabled){
35275             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
35276                this.fly(hd).addClass("x-grid-hd-over");
35277             }
35278         }
35279     },
35280
35281     handleHdOut : function(e){
35282         var hd = this.findHeaderCell(e.getTarget());
35283         if(hd){
35284             this.fly(hd).removeClass("x-grid-hd-over");
35285         }
35286     },
35287
35288     handleSplitDblClick : function(e, t){
35289         var i = this.getCellIndex(t);
35290         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
35291             this.autoSizeColumn(i, true);
35292             this.layout();
35293         }
35294     },
35295
35296     render : function(){
35297
35298         var cm = this.cm;
35299         var colCount = cm.getColumnCount();
35300
35301         if(this.grid.monitorWindowResize === true){
35302             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
35303         }
35304         var header = this.renderHeaders();
35305         var body = this.templates.body.apply({rows:""});
35306         var html = this.templates.master.apply({
35307             lockedBody: body,
35308             body: body,
35309             lockedHeader: header[0],
35310             header: header[1]
35311         });
35312
35313         //this.updateColumns();
35314
35315         this.grid.getGridEl().dom.innerHTML = html;
35316
35317         this.initElements();
35318         
35319         // a kludge to fix the random scolling effect in webkit
35320         this.el.on("scroll", function() {
35321             this.el.dom.scrollTop=0; // hopefully not recursive..
35322         },this);
35323
35324         this.scroller.on("scroll", this.handleScroll, this);
35325         this.lockedBody.on("mousewheel", this.handleWheel, this);
35326         this.mainBody.on("mousewheel", this.handleWheel, this);
35327
35328         this.mainHd.on("mouseover", this.handleHdOver, this);
35329         this.mainHd.on("mouseout", this.handleHdOut, this);
35330         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
35331                 {delegate: "."+this.splitClass});
35332
35333         this.lockedHd.on("mouseover", this.handleHdOver, this);
35334         this.lockedHd.on("mouseout", this.handleHdOut, this);
35335         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
35336                 {delegate: "."+this.splitClass});
35337
35338         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
35339             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
35340         }
35341
35342         this.updateSplitters();
35343
35344         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
35345             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
35346             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
35347         }
35348
35349         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
35350             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
35351             this.hmenu.add(
35352                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
35353                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
35354             );
35355             if(this.grid.enableColLock !== false){
35356                 this.hmenu.add('-',
35357                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
35358                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
35359                 );
35360             }
35361             if(this.grid.enableColumnHide !== false){
35362
35363                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
35364                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
35365                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
35366
35367                 this.hmenu.add('-',
35368                     {id:"columns", text: this.columnsText, menu: this.colMenu}
35369                 );
35370             }
35371             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
35372
35373             this.grid.on("headercontextmenu", this.handleHdCtx, this);
35374         }
35375
35376         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
35377             this.dd = new Roo.grid.GridDragZone(this.grid, {
35378                 ddGroup : this.grid.ddGroup || 'GridDD'
35379             });
35380         }
35381
35382         /*
35383         for(var i = 0; i < colCount; i++){
35384             if(cm.isHidden(i)){
35385                 this.hideColumn(i);
35386             }
35387             if(cm.config[i].align){
35388                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
35389                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
35390             }
35391         }*/
35392         
35393         this.updateHeaderSortState();
35394
35395         this.beforeInitialResize();
35396         this.layout(true);
35397
35398         // two part rendering gives faster view to the user
35399         this.renderPhase2.defer(1, this);
35400     },
35401
35402     renderPhase2 : function(){
35403         // render the rows now
35404         this.refresh();
35405         if(this.grid.autoSizeColumns){
35406             this.autoSizeColumns();
35407         }
35408     },
35409
35410     beforeInitialResize : function(){
35411
35412     },
35413
35414     onColumnSplitterMoved : function(i, w){
35415         this.userResized = true;
35416         var cm = this.grid.colModel;
35417         cm.setColumnWidth(i, w, true);
35418         var cid = cm.getColumnId(i);
35419         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
35420         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
35421         this.updateSplitters();
35422         this.layout();
35423         this.grid.fireEvent("columnresize", i, w);
35424     },
35425
35426     syncRowHeights : function(startIndex, endIndex){
35427         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
35428             startIndex = startIndex || 0;
35429             var mrows = this.getBodyTable().rows;
35430             var lrows = this.getLockedTable().rows;
35431             var len = mrows.length-1;
35432             endIndex = Math.min(endIndex || len, len);
35433             for(var i = startIndex; i <= endIndex; i++){
35434                 var m = mrows[i], l = lrows[i];
35435                 var h = Math.max(m.offsetHeight, l.offsetHeight);
35436                 m.style.height = l.style.height = h + "px";
35437             }
35438         }
35439     },
35440
35441     layout : function(initialRender, is2ndPass){
35442         var g = this.grid;
35443         var auto = g.autoHeight;
35444         var scrollOffset = 16;
35445         var c = g.getGridEl(), cm = this.cm,
35446                 expandCol = g.autoExpandColumn,
35447                 gv = this;
35448         //c.beginMeasure();
35449
35450         if(!c.dom.offsetWidth){ // display:none?
35451             if(initialRender){
35452                 this.lockedWrap.show();
35453                 this.mainWrap.show();
35454             }
35455             return;
35456         }
35457
35458         var hasLock = this.cm.isLocked(0);
35459
35460         var tbh = this.headerPanel.getHeight();
35461         var bbh = this.footerPanel.getHeight();
35462
35463         if(auto){
35464             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
35465             var newHeight = ch + c.getBorderWidth("tb");
35466             if(g.maxHeight){
35467                 newHeight = Math.min(g.maxHeight, newHeight);
35468             }
35469             c.setHeight(newHeight);
35470         }
35471
35472         if(g.autoWidth){
35473             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
35474         }
35475
35476         var s = this.scroller;
35477
35478         var csize = c.getSize(true);
35479
35480         this.el.setSize(csize.width, csize.height);
35481
35482         this.headerPanel.setWidth(csize.width);
35483         this.footerPanel.setWidth(csize.width);
35484
35485         var hdHeight = this.mainHd.getHeight();
35486         var vw = csize.width;
35487         var vh = csize.height - (tbh + bbh);
35488
35489         s.setSize(vw, vh);
35490
35491         var bt = this.getBodyTable();
35492         var ltWidth = hasLock ?
35493                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
35494
35495         var scrollHeight = bt.offsetHeight;
35496         var scrollWidth = ltWidth + bt.offsetWidth;
35497         var vscroll = false, hscroll = false;
35498
35499         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
35500
35501         var lw = this.lockedWrap, mw = this.mainWrap;
35502         var lb = this.lockedBody, mb = this.mainBody;
35503
35504         setTimeout(function(){
35505             var t = s.dom.offsetTop;
35506             var w = s.dom.clientWidth,
35507                 h = s.dom.clientHeight;
35508
35509             lw.setTop(t);
35510             lw.setSize(ltWidth, h);
35511
35512             mw.setLeftTop(ltWidth, t);
35513             mw.setSize(w-ltWidth, h);
35514
35515             lb.setHeight(h-hdHeight);
35516             mb.setHeight(h-hdHeight);
35517
35518             if(is2ndPass !== true && !gv.userResized && expandCol){
35519                 // high speed resize without full column calculation
35520                 
35521                 var ci = cm.getIndexById(expandCol);
35522                 if (ci < 0) {
35523                     ci = cm.findColumnIndex(expandCol);
35524                 }
35525                 ci = Math.max(0, ci); // make sure it's got at least the first col.
35526                 var expandId = cm.getColumnId(ci);
35527                 var  tw = cm.getTotalWidth(false);
35528                 var currentWidth = cm.getColumnWidth(ci);
35529                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
35530                 if(currentWidth != cw){
35531                     cm.setColumnWidth(ci, cw, true);
35532                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
35533                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
35534                     gv.updateSplitters();
35535                     gv.layout(false, true);
35536                 }
35537             }
35538
35539             if(initialRender){
35540                 lw.show();
35541                 mw.show();
35542             }
35543             //c.endMeasure();
35544         }, 10);
35545     },
35546
35547     onWindowResize : function(){
35548         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
35549             return;
35550         }
35551         this.layout();
35552     },
35553
35554     appendFooter : function(parentEl){
35555         return null;
35556     },
35557
35558     sortAscText : "Sort Ascending",
35559     sortDescText : "Sort Descending",
35560     lockText : "Lock Column",
35561     unlockText : "Unlock Column",
35562     columnsText : "Columns"
35563 });
35564
35565
35566 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
35567     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
35568     this.proxy.el.addClass('x-grid3-col-dd');
35569 };
35570
35571 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
35572     handleMouseDown : function(e){
35573
35574     },
35575
35576     callHandleMouseDown : function(e){
35577         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
35578     }
35579 });
35580 /*
35581  * Based on:
35582  * Ext JS Library 1.1.1
35583  * Copyright(c) 2006-2007, Ext JS, LLC.
35584  *
35585  * Originally Released Under LGPL - original licence link has changed is not relivant.
35586  *
35587  * Fork - LGPL
35588  * <script type="text/javascript">
35589  */
35590  
35591 // private
35592 // This is a support class used internally by the Grid components
35593 Roo.grid.SplitDragZone = function(grid, hd, hd2){
35594     this.grid = grid;
35595     this.view = grid.getView();
35596     this.proxy = this.view.resizeProxy;
35597     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
35598         "gridSplitters" + this.grid.getGridEl().id, {
35599         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
35600     });
35601     this.setHandleElId(Roo.id(hd));
35602     this.setOuterHandleElId(Roo.id(hd2));
35603     this.scroll = false;
35604 };
35605 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
35606     fly: Roo.Element.fly,
35607
35608     b4StartDrag : function(x, y){
35609         this.view.headersDisabled = true;
35610         this.proxy.setHeight(this.view.mainWrap.getHeight());
35611         var w = this.cm.getColumnWidth(this.cellIndex);
35612         var minw = Math.max(w-this.grid.minColumnWidth, 0);
35613         this.resetConstraints();
35614         this.setXConstraint(minw, 1000);
35615         this.setYConstraint(0, 0);
35616         this.minX = x - minw;
35617         this.maxX = x + 1000;
35618         this.startPos = x;
35619         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
35620     },
35621
35622
35623     handleMouseDown : function(e){
35624         ev = Roo.EventObject.setEvent(e);
35625         var t = this.fly(ev.getTarget());
35626         if(t.hasClass("x-grid-split")){
35627             this.cellIndex = this.view.getCellIndex(t.dom);
35628             this.split = t.dom;
35629             this.cm = this.grid.colModel;
35630             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
35631                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
35632             }
35633         }
35634     },
35635
35636     endDrag : function(e){
35637         this.view.headersDisabled = false;
35638         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
35639         var diff = endX - this.startPos;
35640         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
35641     },
35642
35643     autoOffset : function(){
35644         this.setDelta(0,0);
35645     }
35646 });/*
35647  * Based on:
35648  * Ext JS Library 1.1.1
35649  * Copyright(c) 2006-2007, Ext JS, LLC.
35650  *
35651  * Originally Released Under LGPL - original licence link has changed is not relivant.
35652  *
35653  * Fork - LGPL
35654  * <script type="text/javascript">
35655  */
35656  
35657 // private
35658 // This is a support class used internally by the Grid components
35659 Roo.grid.GridDragZone = function(grid, config){
35660     this.view = grid.getView();
35661     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
35662     if(this.view.lockedBody){
35663         this.setHandleElId(Roo.id(this.view.mainBody.dom));
35664         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
35665     }
35666     this.scroll = false;
35667     this.grid = grid;
35668     this.ddel = document.createElement('div');
35669     this.ddel.className = 'x-grid-dd-wrap';
35670 };
35671
35672 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
35673     ddGroup : "GridDD",
35674
35675     getDragData : function(e){
35676         var t = Roo.lib.Event.getTarget(e);
35677         var rowIndex = this.view.findRowIndex(t);
35678         if(rowIndex !== false){
35679             var sm = this.grid.selModel;
35680             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
35681               //  sm.mouseDown(e, t);
35682             //}
35683             if (e.hasModifier()){
35684                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
35685             }
35686             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
35687         }
35688         return false;
35689     },
35690
35691     onInitDrag : function(e){
35692         var data = this.dragData;
35693         this.ddel.innerHTML = this.grid.getDragDropText();
35694         this.proxy.update(this.ddel);
35695         // fire start drag?
35696     },
35697
35698     afterRepair : function(){
35699         this.dragging = false;
35700     },
35701
35702     getRepairXY : function(e, data){
35703         return false;
35704     },
35705
35706     onEndDrag : function(data, e){
35707         // fire end drag?
35708     },
35709
35710     onValidDrop : function(dd, e, id){
35711         // fire drag drop?
35712         this.hideProxy();
35713     },
35714
35715     beforeInvalidDrop : function(e, id){
35716
35717     }
35718 });/*
35719  * Based on:
35720  * Ext JS Library 1.1.1
35721  * Copyright(c) 2006-2007, Ext JS, LLC.
35722  *
35723  * Originally Released Under LGPL - original licence link has changed is not relivant.
35724  *
35725  * Fork - LGPL
35726  * <script type="text/javascript">
35727  */
35728  
35729
35730 /**
35731  * @class Roo.grid.ColumnModel
35732  * @extends Roo.util.Observable
35733  * This is the default implementation of a ColumnModel used by the Grid. It defines
35734  * the columns in the grid.
35735  * <br>Usage:<br>
35736  <pre><code>
35737  var colModel = new Roo.grid.ColumnModel([
35738         {header: "Ticker", width: 60, sortable: true, locked: true},
35739         {header: "Company Name", width: 150, sortable: true},
35740         {header: "Market Cap.", width: 100, sortable: true},
35741         {header: "$ Sales", width: 100, sortable: true, renderer: money},
35742         {header: "Employees", width: 100, sortable: true, resizable: false}
35743  ]);
35744  </code></pre>
35745  * <p>
35746  
35747  * The config options listed for this class are options which may appear in each
35748  * individual column definition.
35749  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
35750  * @constructor
35751  * @param {Object} config An Array of column config objects. See this class's
35752  * config objects for details.
35753 */
35754 Roo.grid.ColumnModel = function(config){
35755         /**
35756      * The config passed into the constructor
35757      */
35758     this.config = config;
35759     this.lookup = {};
35760
35761     // if no id, create one
35762     // if the column does not have a dataIndex mapping,
35763     // map it to the order it is in the config
35764     for(var i = 0, len = config.length; i < len; i++){
35765         var c = config[i];
35766         if(typeof c.dataIndex == "undefined"){
35767             c.dataIndex = i;
35768         }
35769         if(typeof c.renderer == "string"){
35770             c.renderer = Roo.util.Format[c.renderer];
35771         }
35772         if(typeof c.id == "undefined"){
35773             c.id = Roo.id();
35774         }
35775         if(c.editor && c.editor.xtype){
35776             c.editor  = Roo.factory(c.editor, Roo.grid);
35777         }
35778         if(c.editor && c.editor.isFormField){
35779             c.editor = new Roo.grid.GridEditor(c.editor);
35780         }
35781         this.lookup[c.id] = c;
35782     }
35783
35784     /**
35785      * The width of columns which have no width specified (defaults to 100)
35786      * @type Number
35787      */
35788     this.defaultWidth = 100;
35789
35790     /**
35791      * Default sortable of columns which have no sortable specified (defaults to false)
35792      * @type Boolean
35793      */
35794     this.defaultSortable = false;
35795
35796     this.addEvents({
35797         /**
35798              * @event widthchange
35799              * Fires when the width of a column changes.
35800              * @param {ColumnModel} this
35801              * @param {Number} columnIndex The column index
35802              * @param {Number} newWidth The new width
35803              */
35804             "widthchange": true,
35805         /**
35806              * @event headerchange
35807              * Fires when the text of a header changes.
35808              * @param {ColumnModel} this
35809              * @param {Number} columnIndex The column index
35810              * @param {Number} newText The new header text
35811              */
35812             "headerchange": true,
35813         /**
35814              * @event hiddenchange
35815              * Fires when a column is hidden or "unhidden".
35816              * @param {ColumnModel} this
35817              * @param {Number} columnIndex The column index
35818              * @param {Boolean} hidden true if hidden, false otherwise
35819              */
35820             "hiddenchange": true,
35821             /**
35822          * @event columnmoved
35823          * Fires when a column is moved.
35824          * @param {ColumnModel} this
35825          * @param {Number} oldIndex
35826          * @param {Number} newIndex
35827          */
35828         "columnmoved" : true,
35829         /**
35830          * @event columlockchange
35831          * Fires when a column's locked state is changed
35832          * @param {ColumnModel} this
35833          * @param {Number} colIndex
35834          * @param {Boolean} locked true if locked
35835          */
35836         "columnlockchange" : true
35837     });
35838     Roo.grid.ColumnModel.superclass.constructor.call(this);
35839 };
35840 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
35841     /**
35842      * @cfg {String} header The header text to display in the Grid view.
35843      */
35844     /**
35845      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
35846      * {@link Roo.data.Record} definition from which to draw the column's value. If not
35847      * specified, the column's index is used as an index into the Record's data Array.
35848      */
35849     /**
35850      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
35851      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
35852      */
35853     /**
35854      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
35855      * Defaults to the value of the {@link #defaultSortable} property.
35856      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
35857      */
35858     /**
35859      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
35860      */
35861     /**
35862      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
35863      */
35864     /**
35865      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
35866      */
35867     /**
35868      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
35869      */
35870     /**
35871      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
35872      * given the cell's data value. See {@link #setRenderer}. If not specified, the
35873      * default renderer uses the raw data value.
35874      */
35875        /**
35876      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
35877      */
35878     /**
35879      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
35880      */
35881
35882     /**
35883      * Returns the id of the column at the specified index.
35884      * @param {Number} index The column index
35885      * @return {String} the id
35886      */
35887     getColumnId : function(index){
35888         return this.config[index].id;
35889     },
35890
35891     /**
35892      * Returns the column for a specified id.
35893      * @param {String} id The column id
35894      * @return {Object} the column
35895      */
35896     getColumnById : function(id){
35897         return this.lookup[id];
35898     },
35899
35900     
35901     /**
35902      * Returns the column for a specified dataIndex.
35903      * @param {String} dataIndex The column dataIndex
35904      * @return {Object|Boolean} the column or false if not found
35905      */
35906     getColumnByDataIndex: function(dataIndex){
35907         var index = this.findColumnIndex(dataIndex);
35908         return index > -1 ? this.config[index] : false;
35909     },
35910     
35911     /**
35912      * Returns the index for a specified column id.
35913      * @param {String} id The column id
35914      * @return {Number} the index, or -1 if not found
35915      */
35916     getIndexById : function(id){
35917         for(var i = 0, len = this.config.length; i < len; i++){
35918             if(this.config[i].id == id){
35919                 return i;
35920             }
35921         }
35922         return -1;
35923     },
35924     
35925     /**
35926      * Returns the index for a specified column dataIndex.
35927      * @param {String} dataIndex The column dataIndex
35928      * @return {Number} the index, or -1 if not found
35929      */
35930     
35931     findColumnIndex : function(dataIndex){
35932         for(var i = 0, len = this.config.length; i < len; i++){
35933             if(this.config[i].dataIndex == dataIndex){
35934                 return i;
35935             }
35936         }
35937         return -1;
35938     },
35939     
35940     
35941     moveColumn : function(oldIndex, newIndex){
35942         var c = this.config[oldIndex];
35943         this.config.splice(oldIndex, 1);
35944         this.config.splice(newIndex, 0, c);
35945         this.dataMap = null;
35946         this.fireEvent("columnmoved", this, oldIndex, newIndex);
35947     },
35948
35949     isLocked : function(colIndex){
35950         return this.config[colIndex].locked === true;
35951     },
35952
35953     setLocked : function(colIndex, value, suppressEvent){
35954         if(this.isLocked(colIndex) == value){
35955             return;
35956         }
35957         this.config[colIndex].locked = value;
35958         if(!suppressEvent){
35959             this.fireEvent("columnlockchange", this, colIndex, value);
35960         }
35961     },
35962
35963     getTotalLockedWidth : function(){
35964         var totalWidth = 0;
35965         for(var i = 0; i < this.config.length; i++){
35966             if(this.isLocked(i) && !this.isHidden(i)){
35967                 this.totalWidth += this.getColumnWidth(i);
35968             }
35969         }
35970         return totalWidth;
35971     },
35972
35973     getLockedCount : function(){
35974         for(var i = 0, len = this.config.length; i < len; i++){
35975             if(!this.isLocked(i)){
35976                 return i;
35977             }
35978         }
35979     },
35980
35981     /**
35982      * Returns the number of columns.
35983      * @return {Number}
35984      */
35985     getColumnCount : function(visibleOnly){
35986         if(visibleOnly === true){
35987             var c = 0;
35988             for(var i = 0, len = this.config.length; i < len; i++){
35989                 if(!this.isHidden(i)){
35990                     c++;
35991                 }
35992             }
35993             return c;
35994         }
35995         return this.config.length;
35996     },
35997
35998     /**
35999      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
36000      * @param {Function} fn
36001      * @param {Object} scope (optional)
36002      * @return {Array} result
36003      */
36004     getColumnsBy : function(fn, scope){
36005         var r = [];
36006         for(var i = 0, len = this.config.length; i < len; i++){
36007             var c = this.config[i];
36008             if(fn.call(scope||this, c, i) === true){
36009                 r[r.length] = c;
36010             }
36011         }
36012         return r;
36013     },
36014
36015     /**
36016      * Returns true if the specified column is sortable.
36017      * @param {Number} col The column index
36018      * @return {Boolean}
36019      */
36020     isSortable : function(col){
36021         if(typeof this.config[col].sortable == "undefined"){
36022             return this.defaultSortable;
36023         }
36024         return this.config[col].sortable;
36025     },
36026
36027     /**
36028      * Returns the rendering (formatting) function defined for the column.
36029      * @param {Number} col The column index.
36030      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
36031      */
36032     getRenderer : function(col){
36033         if(!this.config[col].renderer){
36034             return Roo.grid.ColumnModel.defaultRenderer;
36035         }
36036         return this.config[col].renderer;
36037     },
36038
36039     /**
36040      * Sets the rendering (formatting) function for a column.
36041      * @param {Number} col The column index
36042      * @param {Function} fn The function to use to process the cell's raw data
36043      * to return HTML markup for the grid view. The render function is called with
36044      * the following parameters:<ul>
36045      * <li>Data value.</li>
36046      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
36047      * <li>css A CSS style string to apply to the table cell.</li>
36048      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
36049      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
36050      * <li>Row index</li>
36051      * <li>Column index</li>
36052      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
36053      */
36054     setRenderer : function(col, fn){
36055         this.config[col].renderer = fn;
36056     },
36057
36058     /**
36059      * Returns the width for the specified column.
36060      * @param {Number} col The column index
36061      * @return {Number}
36062      */
36063     getColumnWidth : function(col){
36064         return this.config[col].width * 1 || this.defaultWidth;
36065     },
36066
36067     /**
36068      * Sets the width for a column.
36069      * @param {Number} col The column index
36070      * @param {Number} width The new width
36071      */
36072     setColumnWidth : function(col, width, suppressEvent){
36073         this.config[col].width = width;
36074         this.totalWidth = null;
36075         if(!suppressEvent){
36076              this.fireEvent("widthchange", this, col, width);
36077         }
36078     },
36079
36080     /**
36081      * Returns the total width of all columns.
36082      * @param {Boolean} includeHidden True to include hidden column widths
36083      * @return {Number}
36084      */
36085     getTotalWidth : function(includeHidden){
36086         if(!this.totalWidth){
36087             this.totalWidth = 0;
36088             for(var i = 0, len = this.config.length; i < len; i++){
36089                 if(includeHidden || !this.isHidden(i)){
36090                     this.totalWidth += this.getColumnWidth(i);
36091                 }
36092             }
36093         }
36094         return this.totalWidth;
36095     },
36096
36097     /**
36098      * Returns the header for the specified column.
36099      * @param {Number} col The column index
36100      * @return {String}
36101      */
36102     getColumnHeader : function(col){
36103         return this.config[col].header;
36104     },
36105
36106     /**
36107      * Sets the header for a column.
36108      * @param {Number} col The column index
36109      * @param {String} header The new header
36110      */
36111     setColumnHeader : function(col, header){
36112         this.config[col].header = header;
36113         this.fireEvent("headerchange", this, col, header);
36114     },
36115
36116     /**
36117      * Returns the tooltip for the specified column.
36118      * @param {Number} col The column index
36119      * @return {String}
36120      */
36121     getColumnTooltip : function(col){
36122             return this.config[col].tooltip;
36123     },
36124     /**
36125      * Sets the tooltip for a column.
36126      * @param {Number} col The column index
36127      * @param {String} tooltip The new tooltip
36128      */
36129     setColumnTooltip : function(col, tooltip){
36130             this.config[col].tooltip = tooltip;
36131     },
36132
36133     /**
36134      * Returns the dataIndex for the specified column.
36135      * @param {Number} col The column index
36136      * @return {Number}
36137      */
36138     getDataIndex : function(col){
36139         return this.config[col].dataIndex;
36140     },
36141
36142     /**
36143      * Sets the dataIndex for a column.
36144      * @param {Number} col The column index
36145      * @param {Number} dataIndex The new dataIndex
36146      */
36147     setDataIndex : function(col, dataIndex){
36148         this.config[col].dataIndex = dataIndex;
36149     },
36150
36151     
36152     
36153     /**
36154      * Returns true if the cell is editable.
36155      * @param {Number} colIndex The column index
36156      * @param {Number} rowIndex The row index
36157      * @return {Boolean}
36158      */
36159     isCellEditable : function(colIndex, rowIndex){
36160         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
36161     },
36162
36163     /**
36164      * Returns the editor defined for the cell/column.
36165      * return false or null to disable editing.
36166      * @param {Number} colIndex The column index
36167      * @param {Number} rowIndex The row index
36168      * @return {Object}
36169      */
36170     getCellEditor : function(colIndex, rowIndex){
36171         return this.config[colIndex].editor;
36172     },
36173
36174     /**
36175      * Sets if a column is editable.
36176      * @param {Number} col The column index
36177      * @param {Boolean} editable True if the column is editable
36178      */
36179     setEditable : function(col, editable){
36180         this.config[col].editable = editable;
36181     },
36182
36183
36184     /**
36185      * Returns true if the column is hidden.
36186      * @param {Number} colIndex The column index
36187      * @return {Boolean}
36188      */
36189     isHidden : function(colIndex){
36190         return this.config[colIndex].hidden;
36191     },
36192
36193
36194     /**
36195      * Returns true if the column width cannot be changed
36196      */
36197     isFixed : function(colIndex){
36198         return this.config[colIndex].fixed;
36199     },
36200
36201     /**
36202      * Returns true if the column can be resized
36203      * @return {Boolean}
36204      */
36205     isResizable : function(colIndex){
36206         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
36207     },
36208     /**
36209      * Sets if a column is hidden.
36210      * @param {Number} colIndex The column index
36211      * @param {Boolean} hidden True if the column is hidden
36212      */
36213     setHidden : function(colIndex, hidden){
36214         this.config[colIndex].hidden = hidden;
36215         this.totalWidth = null;
36216         this.fireEvent("hiddenchange", this, colIndex, hidden);
36217     },
36218
36219     /**
36220      * Sets the editor for a column.
36221      * @param {Number} col The column index
36222      * @param {Object} editor The editor object
36223      */
36224     setEditor : function(col, editor){
36225         this.config[col].editor = editor;
36226     }
36227 });
36228
36229 Roo.grid.ColumnModel.defaultRenderer = function(value){
36230         if(typeof value == "string" && value.length < 1){
36231             return "&#160;";
36232         }
36233         return value;
36234 };
36235
36236 // Alias for backwards compatibility
36237 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
36238 /*
36239  * Based on:
36240  * Ext JS Library 1.1.1
36241  * Copyright(c) 2006-2007, Ext JS, LLC.
36242  *
36243  * Originally Released Under LGPL - original licence link has changed is not relivant.
36244  *
36245  * Fork - LGPL
36246  * <script type="text/javascript">
36247  */
36248
36249 /**
36250  * @class Roo.grid.AbstractSelectionModel
36251  * @extends Roo.util.Observable
36252  * Abstract base class for grid SelectionModels.  It provides the interface that should be
36253  * implemented by descendant classes.  This class should not be directly instantiated.
36254  * @constructor
36255  */
36256 Roo.grid.AbstractSelectionModel = function(){
36257     this.locked = false;
36258     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
36259 };
36260
36261 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
36262     /** @ignore Called by the grid automatically. Do not call directly. */
36263     init : function(grid){
36264         this.grid = grid;
36265         this.initEvents();
36266     },
36267
36268     /**
36269      * Locks the selections.
36270      */
36271     lock : function(){
36272         this.locked = true;
36273     },
36274
36275     /**
36276      * Unlocks the selections.
36277      */
36278     unlock : function(){
36279         this.locked = false;
36280     },
36281
36282     /**
36283      * Returns true if the selections are locked.
36284      * @return {Boolean}
36285      */
36286     isLocked : function(){
36287         return this.locked;
36288     }
36289 });/*
36290  * Based on:
36291  * Ext JS Library 1.1.1
36292  * Copyright(c) 2006-2007, Ext JS, LLC.
36293  *
36294  * Originally Released Under LGPL - original licence link has changed is not relivant.
36295  *
36296  * Fork - LGPL
36297  * <script type="text/javascript">
36298  */
36299 /**
36300  * @extends Roo.grid.AbstractSelectionModel
36301  * @class Roo.grid.RowSelectionModel
36302  * The default SelectionModel used by {@link Roo.grid.Grid}.
36303  * It supports multiple selections and keyboard selection/navigation. 
36304  * @constructor
36305  * @param {Object} config
36306  */
36307 Roo.grid.RowSelectionModel = function(config){
36308     Roo.apply(this, config);
36309     this.selections = new Roo.util.MixedCollection(false, function(o){
36310         return o.id;
36311     });
36312
36313     this.last = false;
36314     this.lastActive = false;
36315
36316     this.addEvents({
36317         /**
36318              * @event selectionchange
36319              * Fires when the selection changes
36320              * @param {SelectionModel} this
36321              */
36322             "selectionchange" : true,
36323         /**
36324              * @event afterselectionchange
36325              * Fires after the selection changes (eg. by key press or clicking)
36326              * @param {SelectionModel} this
36327              */
36328             "afterselectionchange" : true,
36329         /**
36330              * @event beforerowselect
36331              * Fires when a row is selected being selected, return false to cancel.
36332              * @param {SelectionModel} this
36333              * @param {Number} rowIndex The selected index
36334              * @param {Boolean} keepExisting False if other selections will be cleared
36335              */
36336             "beforerowselect" : true,
36337         /**
36338              * @event rowselect
36339              * Fires when a row is selected.
36340              * @param {SelectionModel} this
36341              * @param {Number} rowIndex The selected index
36342              * @param {Roo.data.Record} r The record
36343              */
36344             "rowselect" : true,
36345         /**
36346              * @event rowdeselect
36347              * Fires when a row is deselected.
36348              * @param {SelectionModel} this
36349              * @param {Number} rowIndex The selected index
36350              */
36351         "rowdeselect" : true
36352     });
36353     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
36354     this.locked = false;
36355 };
36356
36357 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
36358     /**
36359      * @cfg {Boolean} singleSelect
36360      * True to allow selection of only one row at a time (defaults to false)
36361      */
36362     singleSelect : false,
36363
36364     // private
36365     initEvents : function(){
36366
36367         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
36368             this.grid.on("mousedown", this.handleMouseDown, this);
36369         }else{ // allow click to work like normal
36370             this.grid.on("rowclick", this.handleDragableRowClick, this);
36371         }
36372
36373         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
36374             "up" : function(e){
36375                 if(!e.shiftKey){
36376                     this.selectPrevious(e.shiftKey);
36377                 }else if(this.last !== false && this.lastActive !== false){
36378                     var last = this.last;
36379                     this.selectRange(this.last,  this.lastActive-1);
36380                     this.grid.getView().focusRow(this.lastActive);
36381                     if(last !== false){
36382                         this.last = last;
36383                     }
36384                 }else{
36385                     this.selectFirstRow();
36386                 }
36387                 this.fireEvent("afterselectionchange", this);
36388             },
36389             "down" : function(e){
36390                 if(!e.shiftKey){
36391                     this.selectNext(e.shiftKey);
36392                 }else if(this.last !== false && this.lastActive !== false){
36393                     var last = this.last;
36394                     this.selectRange(this.last,  this.lastActive+1);
36395                     this.grid.getView().focusRow(this.lastActive);
36396                     if(last !== false){
36397                         this.last = last;
36398                     }
36399                 }else{
36400                     this.selectFirstRow();
36401                 }
36402                 this.fireEvent("afterselectionchange", this);
36403             },
36404             scope: this
36405         });
36406
36407         var view = this.grid.view;
36408         view.on("refresh", this.onRefresh, this);
36409         view.on("rowupdated", this.onRowUpdated, this);
36410         view.on("rowremoved", this.onRemove, this);
36411     },
36412
36413     // private
36414     onRefresh : function(){
36415         var ds = this.grid.dataSource, i, v = this.grid.view;
36416         var s = this.selections;
36417         s.each(function(r){
36418             if((i = ds.indexOfId(r.id)) != -1){
36419                 v.onRowSelect(i);
36420             }else{
36421                 s.remove(r);
36422             }
36423         });
36424     },
36425
36426     // private
36427     onRemove : function(v, index, r){
36428         this.selections.remove(r);
36429     },
36430
36431     // private
36432     onRowUpdated : function(v, index, r){
36433         if(this.isSelected(r)){
36434             v.onRowSelect(index);
36435         }
36436     },
36437
36438     /**
36439      * Select records.
36440      * @param {Array} records The records to select
36441      * @param {Boolean} keepExisting (optional) True to keep existing selections
36442      */
36443     selectRecords : function(records, keepExisting){
36444         if(!keepExisting){
36445             this.clearSelections();
36446         }
36447         var ds = this.grid.dataSource;
36448         for(var i = 0, len = records.length; i < len; i++){
36449             this.selectRow(ds.indexOf(records[i]), true);
36450         }
36451     },
36452
36453     /**
36454      * Gets the number of selected rows.
36455      * @return {Number}
36456      */
36457     getCount : function(){
36458         return this.selections.length;
36459     },
36460
36461     /**
36462      * Selects the first row in the grid.
36463      */
36464     selectFirstRow : function(){
36465         this.selectRow(0);
36466     },
36467
36468     /**
36469      * Select the last row.
36470      * @param {Boolean} keepExisting (optional) True to keep existing selections
36471      */
36472     selectLastRow : function(keepExisting){
36473         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
36474     },
36475
36476     /**
36477      * Selects the row immediately following the last selected row.
36478      * @param {Boolean} keepExisting (optional) True to keep existing selections
36479      */
36480     selectNext : function(keepExisting){
36481         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
36482             this.selectRow(this.last+1, keepExisting);
36483             this.grid.getView().focusRow(this.last);
36484         }
36485     },
36486
36487     /**
36488      * Selects the row that precedes the last selected row.
36489      * @param {Boolean} keepExisting (optional) True to keep existing selections
36490      */
36491     selectPrevious : function(keepExisting){
36492         if(this.last){
36493             this.selectRow(this.last-1, keepExisting);
36494             this.grid.getView().focusRow(this.last);
36495         }
36496     },
36497
36498     /**
36499      * Returns the selected records
36500      * @return {Array} Array of selected records
36501      */
36502     getSelections : function(){
36503         return [].concat(this.selections.items);
36504     },
36505
36506     /**
36507      * Returns the first selected record.
36508      * @return {Record}
36509      */
36510     getSelected : function(){
36511         return this.selections.itemAt(0);
36512     },
36513
36514
36515     /**
36516      * Clears all selections.
36517      */
36518     clearSelections : function(fast){
36519         if(this.locked) return;
36520         if(fast !== true){
36521             var ds = this.grid.dataSource;
36522             var s = this.selections;
36523             s.each(function(r){
36524                 this.deselectRow(ds.indexOfId(r.id));
36525             }, this);
36526             s.clear();
36527         }else{
36528             this.selections.clear();
36529         }
36530         this.last = false;
36531     },
36532
36533
36534     /**
36535      * Selects all rows.
36536      */
36537     selectAll : function(){
36538         if(this.locked) return;
36539         this.selections.clear();
36540         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
36541             this.selectRow(i, true);
36542         }
36543     },
36544
36545     /**
36546      * Returns True if there is a selection.
36547      * @return {Boolean}
36548      */
36549     hasSelection : function(){
36550         return this.selections.length > 0;
36551     },
36552
36553     /**
36554      * Returns True if the specified row is selected.
36555      * @param {Number/Record} record The record or index of the record to check
36556      * @return {Boolean}
36557      */
36558     isSelected : function(index){
36559         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
36560         return (r && this.selections.key(r.id) ? true : false);
36561     },
36562
36563     /**
36564      * Returns True if the specified record id is selected.
36565      * @param {String} id The id of record to check
36566      * @return {Boolean}
36567      */
36568     isIdSelected : function(id){
36569         return (this.selections.key(id) ? true : false);
36570     },
36571
36572     // private
36573     handleMouseDown : function(e, t){
36574         var view = this.grid.getView(), rowIndex;
36575         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
36576             return;
36577         };
36578         if(e.shiftKey && this.last !== false){
36579             var last = this.last;
36580             this.selectRange(last, rowIndex, e.ctrlKey);
36581             this.last = last; // reset the last
36582             view.focusRow(rowIndex);
36583         }else{
36584             var isSelected = this.isSelected(rowIndex);
36585             if(e.button !== 0 && isSelected){
36586                 view.focusRow(rowIndex);
36587             }else if(e.ctrlKey && isSelected){
36588                 this.deselectRow(rowIndex);
36589             }else if(!isSelected){
36590                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
36591                 view.focusRow(rowIndex);
36592             }
36593         }
36594         this.fireEvent("afterselectionchange", this);
36595     },
36596     // private
36597     handleDragableRowClick :  function(grid, rowIndex, e) 
36598     {
36599         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
36600             this.selectRow(rowIndex, false);
36601             grid.view.focusRow(rowIndex);
36602              this.fireEvent("afterselectionchange", this);
36603         }
36604     },
36605     
36606     /**
36607      * Selects multiple rows.
36608      * @param {Array} rows Array of the indexes of the row to select
36609      * @param {Boolean} keepExisting (optional) True to keep existing selections
36610      */
36611     selectRows : function(rows, keepExisting){
36612         if(!keepExisting){
36613             this.clearSelections();
36614         }
36615         for(var i = 0, len = rows.length; i < len; i++){
36616             this.selectRow(rows[i], true);
36617         }
36618     },
36619
36620     /**
36621      * Selects a range of rows. All rows in between startRow and endRow are also selected.
36622      * @param {Number} startRow The index of the first row in the range
36623      * @param {Number} endRow The index of the last row in the range
36624      * @param {Boolean} keepExisting (optional) True to retain existing selections
36625      */
36626     selectRange : function(startRow, endRow, keepExisting){
36627         if(this.locked) return;
36628         if(!keepExisting){
36629             this.clearSelections();
36630         }
36631         if(startRow <= endRow){
36632             for(var i = startRow; i <= endRow; i++){
36633                 this.selectRow(i, true);
36634             }
36635         }else{
36636             for(var i = startRow; i >= endRow; i--){
36637                 this.selectRow(i, true);
36638             }
36639         }
36640     },
36641
36642     /**
36643      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
36644      * @param {Number} startRow The index of the first row in the range
36645      * @param {Number} endRow The index of the last row in the range
36646      */
36647     deselectRange : function(startRow, endRow, preventViewNotify){
36648         if(this.locked) return;
36649         for(var i = startRow; i <= endRow; i++){
36650             this.deselectRow(i, preventViewNotify);
36651         }
36652     },
36653
36654     /**
36655      * Selects a row.
36656      * @param {Number} row The index of the row to select
36657      * @param {Boolean} keepExisting (optional) True to keep existing selections
36658      */
36659     selectRow : function(index, keepExisting, preventViewNotify){
36660         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
36661         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
36662             if(!keepExisting || this.singleSelect){
36663                 this.clearSelections();
36664             }
36665             var r = this.grid.dataSource.getAt(index);
36666             this.selections.add(r);
36667             this.last = this.lastActive = index;
36668             if(!preventViewNotify){
36669                 this.grid.getView().onRowSelect(index);
36670             }
36671             this.fireEvent("rowselect", this, index, r);
36672             this.fireEvent("selectionchange", this);
36673         }
36674     },
36675
36676     /**
36677      * Deselects a row.
36678      * @param {Number} row The index of the row to deselect
36679      */
36680     deselectRow : function(index, preventViewNotify){
36681         if(this.locked) return;
36682         if(this.last == index){
36683             this.last = false;
36684         }
36685         if(this.lastActive == index){
36686             this.lastActive = false;
36687         }
36688         var r = this.grid.dataSource.getAt(index);
36689         this.selections.remove(r);
36690         if(!preventViewNotify){
36691             this.grid.getView().onRowDeselect(index);
36692         }
36693         this.fireEvent("rowdeselect", this, index);
36694         this.fireEvent("selectionchange", this);
36695     },
36696
36697     // private
36698     restoreLast : function(){
36699         if(this._last){
36700             this.last = this._last;
36701         }
36702     },
36703
36704     // private
36705     acceptsNav : function(row, col, cm){
36706         return !cm.isHidden(col) && cm.isCellEditable(col, row);
36707     },
36708
36709     // private
36710     onEditorKey : function(field, e){
36711         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
36712         if(k == e.TAB){
36713             e.stopEvent();
36714             ed.completeEdit();
36715             if(e.shiftKey){
36716                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36717             }else{
36718                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36719             }
36720         }else if(k == e.ENTER && !e.ctrlKey){
36721             e.stopEvent();
36722             ed.completeEdit();
36723             if(e.shiftKey){
36724                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
36725             }else{
36726                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
36727             }
36728         }else if(k == e.ESC){
36729             ed.cancelEdit();
36730         }
36731         if(newCell){
36732             g.startEditing(newCell[0], newCell[1]);
36733         }
36734     }
36735 });/*
36736  * Based on:
36737  * Ext JS Library 1.1.1
36738  * Copyright(c) 2006-2007, Ext JS, LLC.
36739  *
36740  * Originally Released Under LGPL - original licence link has changed is not relivant.
36741  *
36742  * Fork - LGPL
36743  * <script type="text/javascript">
36744  */
36745 /**
36746  * @class Roo.grid.CellSelectionModel
36747  * @extends Roo.grid.AbstractSelectionModel
36748  * This class provides the basic implementation for cell selection in a grid.
36749  * @constructor
36750  * @param {Object} config The object containing the configuration of this model.
36751  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
36752  */
36753 Roo.grid.CellSelectionModel = function(config){
36754     Roo.apply(this, config);
36755
36756     this.selection = null;
36757
36758     this.addEvents({
36759         /**
36760              * @event beforerowselect
36761              * Fires before a cell is selected.
36762              * @param {SelectionModel} this
36763              * @param {Number} rowIndex The selected row index
36764              * @param {Number} colIndex The selected cell index
36765              */
36766             "beforecellselect" : true,
36767         /**
36768              * @event cellselect
36769              * Fires when a cell is selected.
36770              * @param {SelectionModel} this
36771              * @param {Number} rowIndex The selected row index
36772              * @param {Number} colIndex The selected cell index
36773              */
36774             "cellselect" : true,
36775         /**
36776              * @event selectionchange
36777              * Fires when the active selection changes.
36778              * @param {SelectionModel} this
36779              * @param {Object} selection null for no selection or an object (o) with two properties
36780                 <ul>
36781                 <li>o.record: the record object for the row the selection is in</li>
36782                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
36783                 </ul>
36784              */
36785             "selectionchange" : true,
36786         /**
36787              * @event tabend
36788              * Fires when the tab (or enter) was pressed on the last editable cell
36789              * You can use this to trigger add new row.
36790              * @param {SelectionModel} this
36791              */
36792             "tabend" : true
36793     });
36794     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
36795 };
36796
36797 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
36798     
36799     enter_is_tab: false,
36800
36801     /** @ignore */
36802     initEvents : function(){
36803         this.grid.on("mousedown", this.handleMouseDown, this);
36804         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
36805         var view = this.grid.view;
36806         view.on("refresh", this.onViewChange, this);
36807         view.on("rowupdated", this.onRowUpdated, this);
36808         view.on("beforerowremoved", this.clearSelections, this);
36809         view.on("beforerowsinserted", this.clearSelections, this);
36810         if(this.grid.isEditor){
36811             this.grid.on("beforeedit", this.beforeEdit,  this);
36812         }
36813     },
36814
36815         //private
36816     beforeEdit : function(e){
36817         this.select(e.row, e.column, false, true, e.record);
36818     },
36819
36820         //private
36821     onRowUpdated : function(v, index, r){
36822         if(this.selection && this.selection.record == r){
36823             v.onCellSelect(index, this.selection.cell[1]);
36824         }
36825     },
36826
36827         //private
36828     onViewChange : function(){
36829         this.clearSelections(true);
36830     },
36831
36832         /**
36833          * Returns the currently selected cell,.
36834          * @return {Array} The selected cell (row, column) or null if none selected.
36835          */
36836     getSelectedCell : function(){
36837         return this.selection ? this.selection.cell : null;
36838     },
36839
36840     /**
36841      * Clears all selections.
36842      * @param {Boolean} true to prevent the gridview from being notified about the change.
36843      */
36844     clearSelections : function(preventNotify){
36845         var s = this.selection;
36846         if(s){
36847             if(preventNotify !== true){
36848                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
36849             }
36850             this.selection = null;
36851             this.fireEvent("selectionchange", this, null);
36852         }
36853     },
36854
36855     /**
36856      * Returns true if there is a selection.
36857      * @return {Boolean}
36858      */
36859     hasSelection : function(){
36860         return this.selection ? true : false;
36861     },
36862
36863     /** @ignore */
36864     handleMouseDown : function(e, t){
36865         var v = this.grid.getView();
36866         if(this.isLocked()){
36867             return;
36868         };
36869         var row = v.findRowIndex(t);
36870         var cell = v.findCellIndex(t);
36871         if(row !== false && cell !== false){
36872             this.select(row, cell);
36873         }
36874     },
36875
36876     /**
36877      * Selects a cell.
36878      * @param {Number} rowIndex
36879      * @param {Number} collIndex
36880      */
36881     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
36882         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
36883             this.clearSelections();
36884             r = r || this.grid.dataSource.getAt(rowIndex);
36885             this.selection = {
36886                 record : r,
36887                 cell : [rowIndex, colIndex]
36888             };
36889             if(!preventViewNotify){
36890                 var v = this.grid.getView();
36891                 v.onCellSelect(rowIndex, colIndex);
36892                 if(preventFocus !== true){
36893                     v.focusCell(rowIndex, colIndex);
36894                 }
36895             }
36896             this.fireEvent("cellselect", this, rowIndex, colIndex);
36897             this.fireEvent("selectionchange", this, this.selection);
36898         }
36899     },
36900
36901         //private
36902     isSelectable : function(rowIndex, colIndex, cm){
36903         return !cm.isHidden(colIndex);
36904     },
36905
36906     /** @ignore */
36907     handleKeyDown : function(e){
36908         //Roo.log('Cell Sel Model handleKeyDown');
36909         if(!e.isNavKeyPress()){
36910             return;
36911         }
36912         var g = this.grid, s = this.selection;
36913         if(!s){
36914             e.stopEvent();
36915             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
36916             if(cell){
36917                 this.select(cell[0], cell[1]);
36918             }
36919             return;
36920         }
36921         var sm = this;
36922         var walk = function(row, col, step){
36923             return g.walkCells(row, col, step, sm.isSelectable,  sm);
36924         };
36925         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
36926         var newCell;
36927
36928       
36929
36930         switch(k){
36931             case e.TAB:
36932                 // handled by onEditorKey
36933                 if (g.isEditor && g.editing) {
36934                     return;
36935                 }
36936                 if(e.shiftKey) {
36937                     newCell = walk(r, c-1, -1);
36938                 } else {
36939                     newCell = walk(r, c+1, 1);
36940                 }
36941                 break;
36942             
36943             case e.DOWN:
36944                newCell = walk(r+1, c, 1);
36945                 break;
36946             
36947             case e.UP:
36948                 newCell = walk(r-1, c, -1);
36949                 break;
36950             
36951             case e.RIGHT:
36952                 newCell = walk(r, c+1, 1);
36953                 break;
36954             
36955             case e.LEFT:
36956                 newCell = walk(r, c-1, -1);
36957                 break;
36958             
36959             case e.ENTER:
36960                 
36961                 if(g.isEditor && !g.editing){
36962                    g.startEditing(r, c);
36963                    e.stopEvent();
36964                    return;
36965                 }
36966                 
36967                 
36968              break;
36969         };
36970         if(newCell){
36971             this.select(newCell[0], newCell[1]);
36972             e.stopEvent();
36973             
36974         }
36975     },
36976
36977     acceptsNav : function(row, col, cm){
36978         return !cm.isHidden(col) && cm.isCellEditable(col, row);
36979     },
36980     /**
36981      * Selects a cell.
36982      * @param {Number} field (not used) - as it's normally used as a listener
36983      * @param {Number} e - event - fake it by using
36984      *
36985      * var e = Roo.EventObjectImpl.prototype;
36986      * e.keyCode = e.TAB
36987      *
36988      * 
36989      */
36990     onEditorKey : function(field, e){
36991         
36992         var k = e.getKey(),
36993             newCell,
36994             g = this.grid,
36995             ed = g.activeEditor,
36996             forward = false;
36997         ///Roo.log('onEditorKey' + k);
36998         
36999         
37000         if (this.enter_is_tab && k == e.ENTER) {
37001             k = e.TAB;
37002         }
37003         
37004         if(k == e.TAB){
37005             if(e.shiftKey){
37006                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
37007             }else{
37008                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
37009                 forward = true;
37010             }
37011             
37012             e.stopEvent();
37013             
37014         }else if(k == e.ENTER &&  !e.ctrlKey){
37015             ed.completeEdit();
37016             e.stopEvent();
37017             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
37018         }else if(k == e.ESC){
37019             ed.cancelEdit();
37020         }
37021         
37022         
37023         if(newCell){
37024             //Roo.log('next cell after edit');
37025             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
37026         } else if (forward) {
37027             // tabbed past last
37028             this.fireEvent.defer(100, this, ['tabend',this]);
37029         }
37030     }
37031 });/*
37032  * Based on:
37033  * Ext JS Library 1.1.1
37034  * Copyright(c) 2006-2007, Ext JS, LLC.
37035  *
37036  * Originally Released Under LGPL - original licence link has changed is not relivant.
37037  *
37038  * Fork - LGPL
37039  * <script type="text/javascript">
37040  */
37041  
37042 /**
37043  * @class Roo.grid.EditorGrid
37044  * @extends Roo.grid.Grid
37045  * Class for creating and editable grid.
37046  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
37047  * The container MUST have some type of size defined for the grid to fill. The container will be 
37048  * automatically set to position relative if it isn't already.
37049  * @param {Object} dataSource The data model to bind to
37050  * @param {Object} colModel The column model with info about this grid's columns
37051  */
37052 Roo.grid.EditorGrid = function(container, config){
37053     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
37054     this.getGridEl().addClass("xedit-grid");
37055
37056     if(!this.selModel){
37057         this.selModel = new Roo.grid.CellSelectionModel();
37058     }
37059
37060     this.activeEditor = null;
37061
37062         this.addEvents({
37063             /**
37064              * @event beforeedit
37065              * Fires before cell editing is triggered. The edit event object has the following properties <br />
37066              * <ul style="padding:5px;padding-left:16px;">
37067              * <li>grid - This grid</li>
37068              * <li>record - The record being edited</li>
37069              * <li>field - The field name being edited</li>
37070              * <li>value - The value for the field being edited.</li>
37071              * <li>row - The grid row index</li>
37072              * <li>column - The grid column index</li>
37073              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
37074              * </ul>
37075              * @param {Object} e An edit event (see above for description)
37076              */
37077             "beforeedit" : true,
37078             /**
37079              * @event afteredit
37080              * Fires after a cell is edited. <br />
37081              * <ul style="padding:5px;padding-left:16px;">
37082              * <li>grid - This grid</li>
37083              * <li>record - The record being edited</li>
37084              * <li>field - The field name being edited</li>
37085              * <li>value - The value being set</li>
37086              * <li>originalValue - The original value for the field, before the edit.</li>
37087              * <li>row - The grid row index</li>
37088              * <li>column - The grid column index</li>
37089              * </ul>
37090              * @param {Object} e An edit event (see above for description)
37091              */
37092             "afteredit" : true,
37093             /**
37094              * @event validateedit
37095              * Fires after a cell is edited, but before the value is set in the record. 
37096          * You can use this to modify the value being set in the field, Return false
37097              * to cancel the change. The edit event object has the following properties <br />
37098              * <ul style="padding:5px;padding-left:16px;">
37099          * <li>editor - This editor</li>
37100              * <li>grid - This grid</li>
37101              * <li>record - The record being edited</li>
37102              * <li>field - The field name being edited</li>
37103              * <li>value - The value being set</li>
37104              * <li>originalValue - The original value for the field, before the edit.</li>
37105              * <li>row - The grid row index</li>
37106              * <li>column - The grid column index</li>
37107              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
37108              * </ul>
37109              * @param {Object} e An edit event (see above for description)
37110              */
37111             "validateedit" : true
37112         });
37113     this.on("bodyscroll", this.stopEditing,  this);
37114     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
37115 };
37116
37117 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
37118     /**
37119      * @cfg {Number} clicksToEdit
37120      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
37121      */
37122     clicksToEdit: 2,
37123
37124     // private
37125     isEditor : true,
37126     // private
37127     trackMouseOver: false, // causes very odd FF errors
37128
37129     onCellDblClick : function(g, row, col){
37130         this.startEditing(row, col);
37131     },
37132
37133     onEditComplete : function(ed, value, startValue){
37134         this.editing = false;
37135         this.activeEditor = null;
37136         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
37137         var r = ed.record;
37138         var field = this.colModel.getDataIndex(ed.col);
37139         var e = {
37140             grid: this,
37141             record: r,
37142             field: field,
37143             originalValue: startValue,
37144             value: value,
37145             row: ed.row,
37146             column: ed.col,
37147             cancel:false,
37148             editor: ed
37149         };
37150         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
37151         cell.show();
37152           
37153         if(String(value) !== String(startValue)){
37154             
37155             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
37156                 r.set(field, e.value);
37157                 // if we are dealing with a combo box..
37158                 // then we also set the 'name' colum to be the displayField
37159                 if (ed.field.displayField && ed.field.name) {
37160                     r.set(ed.field.name, ed.field.el.dom.value);
37161                 }
37162                 
37163                 delete e.cancel; //?? why!!!
37164                 this.fireEvent("afteredit", e);
37165             }
37166         } else {
37167             this.fireEvent("afteredit", e); // always fire it!
37168         }
37169         this.view.focusCell(ed.row, ed.col);
37170     },
37171
37172     /**
37173      * Starts editing the specified for the specified row/column
37174      * @param {Number} rowIndex
37175      * @param {Number} colIndex
37176      */
37177     startEditing : function(row, col){
37178         this.stopEditing();
37179         if(this.colModel.isCellEditable(col, row)){
37180             this.view.ensureVisible(row, col, true);
37181           
37182             var r = this.dataSource.getAt(row);
37183             var field = this.colModel.getDataIndex(col);
37184             var cell = Roo.get(this.view.getCell(row,col));
37185             var e = {
37186                 grid: this,
37187                 record: r,
37188                 field: field,
37189                 value: r.data[field],
37190                 row: row,
37191                 column: col,
37192                 cancel:false 
37193             };
37194             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
37195                 this.editing = true;
37196                 var ed = this.colModel.getCellEditor(col, row);
37197                 
37198                 if (!ed) {
37199                     return;
37200                 }
37201                 if(!ed.rendered){
37202                     ed.render(ed.parentEl || document.body);
37203                 }
37204                 ed.field.reset();
37205                
37206                 cell.hide();
37207                 
37208                 (function(){ // complex but required for focus issues in safari, ie and opera
37209                     ed.row = row;
37210                     ed.col = col;
37211                     ed.record = r;
37212                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
37213                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
37214                     this.activeEditor = ed;
37215                     var v = r.data[field];
37216                     ed.startEdit(this.view.getCell(row, col), v);
37217                     // combo's with 'displayField and name set
37218                     if (ed.field.displayField && ed.field.name) {
37219                         ed.field.el.dom.value = r.data[ed.field.name];
37220                     }
37221                     
37222                     
37223                 }).defer(50, this);
37224             }
37225         }
37226     },
37227         
37228     /**
37229      * Stops any active editing
37230      */
37231     stopEditing : function(){
37232         if(this.activeEditor){
37233             this.activeEditor.completeEdit();
37234         }
37235         this.activeEditor = null;
37236     }
37237 });/*
37238  * Based on:
37239  * Ext JS Library 1.1.1
37240  * Copyright(c) 2006-2007, Ext JS, LLC.
37241  *
37242  * Originally Released Under LGPL - original licence link has changed is not relivant.
37243  *
37244  * Fork - LGPL
37245  * <script type="text/javascript">
37246  */
37247
37248 // private - not really -- you end up using it !
37249 // This is a support class used internally by the Grid components
37250
37251 /**
37252  * @class Roo.grid.GridEditor
37253  * @extends Roo.Editor
37254  * Class for creating and editable grid elements.
37255  * @param {Object} config any settings (must include field)
37256  */
37257 Roo.grid.GridEditor = function(field, config){
37258     if (!config && field.field) {
37259         config = field;
37260         field = Roo.factory(config.field, Roo.form);
37261     }
37262     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
37263     field.monitorTab = false;
37264 };
37265
37266 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
37267     
37268     /**
37269      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
37270      */
37271     
37272     alignment: "tl-tl",
37273     autoSize: "width",
37274     hideEl : false,
37275     cls: "x-small-editor x-grid-editor",
37276     shim:false,
37277     shadow:"frame"
37278 });/*
37279  * Based on:
37280  * Ext JS Library 1.1.1
37281  * Copyright(c) 2006-2007, Ext JS, LLC.
37282  *
37283  * Originally Released Under LGPL - original licence link has changed is not relivant.
37284  *
37285  * Fork - LGPL
37286  * <script type="text/javascript">
37287  */
37288   
37289
37290   
37291 Roo.grid.PropertyRecord = Roo.data.Record.create([
37292     {name:'name',type:'string'},  'value'
37293 ]);
37294
37295
37296 Roo.grid.PropertyStore = function(grid, source){
37297     this.grid = grid;
37298     this.store = new Roo.data.Store({
37299         recordType : Roo.grid.PropertyRecord
37300     });
37301     this.store.on('update', this.onUpdate,  this);
37302     if(source){
37303         this.setSource(source);
37304     }
37305     Roo.grid.PropertyStore.superclass.constructor.call(this);
37306 };
37307
37308
37309
37310 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
37311     setSource : function(o){
37312         this.source = o;
37313         this.store.removeAll();
37314         var data = [];
37315         for(var k in o){
37316             if(this.isEditableValue(o[k])){
37317                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
37318             }
37319         }
37320         this.store.loadRecords({records: data}, {}, true);
37321     },
37322
37323     onUpdate : function(ds, record, type){
37324         if(type == Roo.data.Record.EDIT){
37325             var v = record.data['value'];
37326             var oldValue = record.modified['value'];
37327             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
37328                 this.source[record.id] = v;
37329                 record.commit();
37330                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
37331             }else{
37332                 record.reject();
37333             }
37334         }
37335     },
37336
37337     getProperty : function(row){
37338        return this.store.getAt(row);
37339     },
37340
37341     isEditableValue: function(val){
37342         if(val && val instanceof Date){
37343             return true;
37344         }else if(typeof val == 'object' || typeof val == 'function'){
37345             return false;
37346         }
37347         return true;
37348     },
37349
37350     setValue : function(prop, value){
37351         this.source[prop] = value;
37352         this.store.getById(prop).set('value', value);
37353     },
37354
37355     getSource : function(){
37356         return this.source;
37357     }
37358 });
37359
37360 Roo.grid.PropertyColumnModel = function(grid, store){
37361     this.grid = grid;
37362     var g = Roo.grid;
37363     g.PropertyColumnModel.superclass.constructor.call(this, [
37364         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
37365         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
37366     ]);
37367     this.store = store;
37368     this.bselect = Roo.DomHelper.append(document.body, {
37369         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
37370             {tag: 'option', value: 'true', html: 'true'},
37371             {tag: 'option', value: 'false', html: 'false'}
37372         ]
37373     });
37374     Roo.id(this.bselect);
37375     var f = Roo.form;
37376     this.editors = {
37377         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
37378         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
37379         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
37380         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
37381         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
37382     };
37383     this.renderCellDelegate = this.renderCell.createDelegate(this);
37384     this.renderPropDelegate = this.renderProp.createDelegate(this);
37385 };
37386
37387 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
37388     
37389     
37390     nameText : 'Name',
37391     valueText : 'Value',
37392     
37393     dateFormat : 'm/j/Y',
37394     
37395     
37396     renderDate : function(dateVal){
37397         return dateVal.dateFormat(this.dateFormat);
37398     },
37399
37400     renderBool : function(bVal){
37401         return bVal ? 'true' : 'false';
37402     },
37403
37404     isCellEditable : function(colIndex, rowIndex){
37405         return colIndex == 1;
37406     },
37407
37408     getRenderer : function(col){
37409         return col == 1 ?
37410             this.renderCellDelegate : this.renderPropDelegate;
37411     },
37412
37413     renderProp : function(v){
37414         return this.getPropertyName(v);
37415     },
37416
37417     renderCell : function(val){
37418         var rv = val;
37419         if(val instanceof Date){
37420             rv = this.renderDate(val);
37421         }else if(typeof val == 'boolean'){
37422             rv = this.renderBool(val);
37423         }
37424         return Roo.util.Format.htmlEncode(rv);
37425     },
37426
37427     getPropertyName : function(name){
37428         var pn = this.grid.propertyNames;
37429         return pn && pn[name] ? pn[name] : name;
37430     },
37431
37432     getCellEditor : function(colIndex, rowIndex){
37433         var p = this.store.getProperty(rowIndex);
37434         var n = p.data['name'], val = p.data['value'];
37435         
37436         if(typeof(this.grid.customEditors[n]) == 'string'){
37437             return this.editors[this.grid.customEditors[n]];
37438         }
37439         if(typeof(this.grid.customEditors[n]) != 'undefined'){
37440             return this.grid.customEditors[n];
37441         }
37442         if(val instanceof Date){
37443             return this.editors['date'];
37444         }else if(typeof val == 'number'){
37445             return this.editors['number'];
37446         }else if(typeof val == 'boolean'){
37447             return this.editors['boolean'];
37448         }else{
37449             return this.editors['string'];
37450         }
37451     }
37452 });
37453
37454 /**
37455  * @class Roo.grid.PropertyGrid
37456  * @extends Roo.grid.EditorGrid
37457  * This class represents the  interface of a component based property grid control.
37458  * <br><br>Usage:<pre><code>
37459  var grid = new Roo.grid.PropertyGrid("my-container-id", {
37460       
37461  });
37462  // set any options
37463  grid.render();
37464  * </code></pre>
37465   
37466  * @constructor
37467  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
37468  * The container MUST have some type of size defined for the grid to fill. The container will be
37469  * automatically set to position relative if it isn't already.
37470  * @param {Object} config A config object that sets properties on this grid.
37471  */
37472 Roo.grid.PropertyGrid = function(container, config){
37473     config = config || {};
37474     var store = new Roo.grid.PropertyStore(this);
37475     this.store = store;
37476     var cm = new Roo.grid.PropertyColumnModel(this, store);
37477     store.store.sort('name', 'ASC');
37478     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
37479         ds: store.store,
37480         cm: cm,
37481         enableColLock:false,
37482         enableColumnMove:false,
37483         stripeRows:false,
37484         trackMouseOver: false,
37485         clicksToEdit:1
37486     }, config));
37487     this.getGridEl().addClass('x-props-grid');
37488     this.lastEditRow = null;
37489     this.on('columnresize', this.onColumnResize, this);
37490     this.addEvents({
37491          /**
37492              * @event beforepropertychange
37493              * Fires before a property changes (return false to stop?)
37494              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
37495              * @param {String} id Record Id
37496              * @param {String} newval New Value
37497          * @param {String} oldval Old Value
37498              */
37499         "beforepropertychange": true,
37500         /**
37501              * @event propertychange
37502              * Fires after a property changes
37503              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
37504              * @param {String} id Record Id
37505              * @param {String} newval New Value
37506          * @param {String} oldval Old Value
37507              */
37508         "propertychange": true
37509     });
37510     this.customEditors = this.customEditors || {};
37511 };
37512 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
37513     
37514      /**
37515      * @cfg {Object} customEditors map of colnames=> custom editors.
37516      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
37517      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
37518      * false disables editing of the field.
37519          */
37520     
37521       /**
37522      * @cfg {Object} propertyNames map of property Names to their displayed value
37523          */
37524     
37525     render : function(){
37526         Roo.grid.PropertyGrid.superclass.render.call(this);
37527         this.autoSize.defer(100, this);
37528     },
37529
37530     autoSize : function(){
37531         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
37532         if(this.view){
37533             this.view.fitColumns();
37534         }
37535     },
37536
37537     onColumnResize : function(){
37538         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
37539         this.autoSize();
37540     },
37541     /**
37542      * Sets the data for the Grid
37543      * accepts a Key => Value object of all the elements avaiable.
37544      * @param {Object} data  to appear in grid.
37545      */
37546     setSource : function(source){
37547         this.store.setSource(source);
37548         //this.autoSize();
37549     },
37550     /**
37551      * Gets all the data from the grid.
37552      * @return {Object} data  data stored in grid
37553      */
37554     getSource : function(){
37555         return this.store.getSource();
37556     }
37557 });/*
37558  * Based on:
37559  * Ext JS Library 1.1.1
37560  * Copyright(c) 2006-2007, Ext JS, LLC.
37561  *
37562  * Originally Released Under LGPL - original licence link has changed is not relivant.
37563  *
37564  * Fork - LGPL
37565  * <script type="text/javascript">
37566  */
37567  
37568 /**
37569  * @class Roo.LoadMask
37570  * A simple utility class for generically masking elements while loading data.  If the element being masked has
37571  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
37572  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
37573  * element's UpdateManager load indicator and will be destroyed after the initial load.
37574  * @constructor
37575  * Create a new LoadMask
37576  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
37577  * @param {Object} config The config object
37578  */
37579 Roo.LoadMask = function(el, config){
37580     this.el = Roo.get(el);
37581     Roo.apply(this, config);
37582     if(this.store){
37583         this.store.on('beforeload', this.onBeforeLoad, this);
37584         this.store.on('load', this.onLoad, this);
37585         this.store.on('loadexception', this.onLoadException, this);
37586         this.removeMask = false;
37587     }else{
37588         var um = this.el.getUpdateManager();
37589         um.showLoadIndicator = false; // disable the default indicator
37590         um.on('beforeupdate', this.onBeforeLoad, this);
37591         um.on('update', this.onLoad, this);
37592         um.on('failure', this.onLoad, this);
37593         this.removeMask = true;
37594     }
37595 };
37596
37597 Roo.LoadMask.prototype = {
37598     /**
37599      * @cfg {Boolean} removeMask
37600      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
37601      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
37602      */
37603     /**
37604      * @cfg {String} msg
37605      * The text to display in a centered loading message box (defaults to 'Loading...')
37606      */
37607     msg : 'Loading...',
37608     /**
37609      * @cfg {String} msgCls
37610      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
37611      */
37612     msgCls : 'x-mask-loading',
37613
37614     /**
37615      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
37616      * @type Boolean
37617      */
37618     disabled: false,
37619
37620     /**
37621      * Disables the mask to prevent it from being displayed
37622      */
37623     disable : function(){
37624        this.disabled = true;
37625     },
37626
37627     /**
37628      * Enables the mask so that it can be displayed
37629      */
37630     enable : function(){
37631         this.disabled = false;
37632     },
37633     
37634     onLoadException : function()
37635     {
37636         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
37637             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
37638         }
37639         this.el.unmask(this.removeMask);
37640     },
37641     // private
37642     onLoad : function()
37643     {
37644         this.el.unmask(this.removeMask);
37645     },
37646
37647     // private
37648     onBeforeLoad : function(){
37649         if(!this.disabled){
37650             this.el.mask(this.msg, this.msgCls);
37651         }
37652     },
37653
37654     // private
37655     destroy : function(){
37656         if(this.store){
37657             this.store.un('beforeload', this.onBeforeLoad, this);
37658             this.store.un('load', this.onLoad, this);
37659             this.store.un('loadexception', this.onLoadException, this);
37660         }else{
37661             var um = this.el.getUpdateManager();
37662             um.un('beforeupdate', this.onBeforeLoad, this);
37663             um.un('update', this.onLoad, this);
37664             um.un('failure', this.onLoad, this);
37665         }
37666     }
37667 };/*
37668  * Based on:
37669  * Ext JS Library 1.1.1
37670  * Copyright(c) 2006-2007, Ext JS, LLC.
37671  *
37672  * Originally Released Under LGPL - original licence link has changed is not relivant.
37673  *
37674  * Fork - LGPL
37675  * <script type="text/javascript">
37676  */
37677 Roo.XTemplate = function(){
37678     Roo.XTemplate.superclass.constructor.apply(this, arguments);
37679     var s = this.html;
37680
37681     s = ['<tpl>', s, '</tpl>'].join('');
37682
37683     var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/;
37684
37685     var nameRe = /^<tpl\b[^>]*?for="(.*?)"/;
37686     var ifRe = /^<tpl\b[^>]*?if="(.*?)"/;
37687     var execRe = /^<tpl\b[^>]*?exec="(.*?)"/;
37688     var m, id = 0;
37689     var tpls = [];
37690
37691     while(m = s.match(re)){
37692        var m2 = m[0].match(nameRe);
37693        var m3 = m[0].match(ifRe);
37694        var m4 = m[0].match(execRe);
37695        var exp = null, fn = null, exec = null;
37696        var name = m2 && m2[1] ? m2[1] : '';
37697        if(m3){
37698            exp = m3 && m3[1] ? m3[1] : null;
37699            if(exp){
37700                fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
37701            }
37702        }
37703        if(m4){
37704            exp = m4 && m4[1] ? m4[1] : null;
37705            if(exp){
37706                exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
37707            }
37708        }
37709        if(name){
37710            switch(name){
37711                case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
37712                case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
37713                default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
37714            }
37715        }
37716        tpls.push({
37717             id: id,
37718             target: name,
37719             exec: exec,
37720             test: fn,
37721             body: m[1]||''
37722         });
37723        s = s.replace(m[0], '{xtpl'+ id + '}');
37724        ++id;
37725     }
37726     for(var i = tpls.length-1; i >= 0; --i){
37727         this.compileTpl(tpls[i]);
37728     }
37729     this.master = tpls[tpls.length-1];
37730     this.tpls = tpls;
37731 };
37732 Roo.extend(Roo.XTemplate, Roo.Template, {
37733
37734     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
37735
37736     applySubTemplate : function(id, values, parent){
37737         var t = this.tpls[id];
37738         if(t.test && !t.test.call(this, values, parent)){
37739             return '';
37740         }
37741         if(t.exec && t.exec.call(this, values, parent)){
37742             return '';
37743         }
37744         var vs = t.target ? t.target.call(this, values, parent) : values;
37745         parent = t.target ? values : parent;
37746         if(t.target && vs instanceof Array){
37747             var buf = [];
37748             for(var i = 0, len = vs.length; i < len; i++){
37749                 buf[buf.length] = t.compiled.call(this, vs[i], parent);
37750             }
37751             return buf.join('');
37752         }
37753         return t.compiled.call(this, vs, parent);
37754     },
37755
37756     compileTpl : function(tpl){
37757         var fm = Roo.util.Format;
37758         var useF = this.disableFormats !== true;
37759         var sep = Roo.isGecko ? "+" : ",";
37760         var fn = function(m, name, format, args){
37761             if(name.substr(0, 4) == 'xtpl'){
37762                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
37763             }
37764             var v;
37765             if(name.indexOf('.') != -1){
37766                 v = name;
37767             }else{
37768                 v = "values['" + name + "']";
37769             }
37770             if(format && useF){
37771                 args = args ? ',' + args : "";
37772                 if(format.substr(0, 5) != "this."){
37773                     format = "fm." + format + '(';
37774                 }else{
37775                     format = 'this.call("'+ format.substr(5) + '", ';
37776                     args = ", values";
37777                 }
37778             }else{
37779                 args= ''; format = "("+v+" === undefined ? '' : ";
37780             }
37781             return "'"+ sep + format + v + args + ")"+sep+"'";
37782         };
37783         var body;
37784         // branched to use + in gecko and [].join() in others
37785         if(Roo.isGecko){
37786             body = "tpl.compiled = function(values, parent){ return '" +
37787                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
37788                     "';};";
37789         }else{
37790             body = ["tpl.compiled = function(values, parent){ return ['"];
37791             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
37792             body.push("'].join('');};");
37793             body = body.join('');
37794         }
37795         /** eval:var:zzzzzzz */
37796         eval(body);
37797         return this;
37798     },
37799
37800     applyTemplate : function(values){
37801         return this.master.compiled.call(this, values, {});
37802         var s = this.subs;
37803     },
37804
37805     apply : function(){
37806         return this.applyTemplate.apply(this, arguments);
37807     },
37808
37809     compile : function(){return this;}
37810 });
37811
37812 Roo.XTemplate.from = function(el){
37813     el = Roo.getDom(el);
37814     return new Roo.XTemplate(el.value || el.innerHTML);
37815 };/*
37816  * Original code for Roojs - LGPL
37817  * <script type="text/javascript">
37818  */
37819  
37820 /**
37821  * @class Roo.XComponent
37822  * A delayed Element creator...
37823  * Or a way to group chunks of interface together.
37824  * 
37825  * Mypart.xyx = new Roo.XComponent({
37826
37827     parent : 'Mypart.xyz', // empty == document.element.!!
37828     order : '001',
37829     name : 'xxxx'
37830     region : 'xxxx'
37831     disabled : function() {} 
37832      
37833     tree : function() { // return an tree of xtype declared components
37834         var MODULE = this;
37835         return 
37836         {
37837             xtype : 'NestedLayoutPanel',
37838             // technicall
37839         }
37840      ]
37841  *})
37842  *
37843  *
37844  * It can be used to build a big heiracy, with parent etc.
37845  * or you can just use this to render a single compoent to a dom element
37846  * MYPART.render(Roo.Element | String(id) | dom_element )
37847  * 
37848  * @extends Roo.util.Observable
37849  * @constructor
37850  * @param cfg {Object} configuration of component
37851  * 
37852  */
37853 Roo.XComponent = function(cfg) {
37854     Roo.apply(this, cfg);
37855     this.addEvents({ 
37856         /**
37857              * @event built
37858              * Fires when this the componnt is built
37859              * @param {Roo.XComponent} c the component
37860              */
37861         'built' : true,
37862         /**
37863              * @event buildcomplete
37864              * Fires on the top level element when all elements have been built
37865              * @param {Roo.XComponent} c the top level component.
37866          */
37867         'buildcomplete' : true
37868         
37869     });
37870     this.region = this.region || 'center'; // default..
37871     Roo.XComponent.register(this);
37872     this.modules = false;
37873     this.el = false; // where the layout goes..
37874     
37875     
37876 }
37877 Roo.extend(Roo.XComponent, Roo.util.Observable, {
37878     /**
37879      * @property el
37880      * The created element (with Roo.factory())
37881      * @type {Roo.Layout}
37882      */
37883     el  : false,
37884     
37885     /**
37886      * @property el
37887      * for BC  - use el in new code
37888      * @type {Roo.Layout}
37889      */
37890     panel : false,
37891     
37892     /**
37893      * @property layout
37894      * for BC  - use el in new code
37895      * @type {Roo.Layout}
37896      */
37897     layout : false,
37898     
37899      /**
37900      * @cfg {Function|boolean} disabled
37901      * If this module is disabled by some rule, return true from the funtion
37902      */
37903     disabled : false,
37904     
37905     /**
37906      * @cfg {String} parent 
37907      * Name of parent element which it get xtype added to..
37908      */
37909     parent: false,
37910     
37911     /**
37912      * @cfg {String} order
37913      * Used to set the order in which elements are created (usefull for multiple tabs)
37914      */
37915     
37916     order : false,
37917     /**
37918      * @cfg {String} name
37919      * String to display while loading.
37920      */
37921     name : false,
37922     /**
37923      * @cfg {String} region
37924      * Region to render component to (defaults to center)
37925      */
37926     region : 'center',
37927     
37928     /**
37929      * @cfg {Array} items
37930      * A single item array - the first element is the root of the tree..
37931      * It's done this way to stay compatible with the Xtype system...
37932      */
37933     items : false,
37934     
37935     
37936      /**
37937      * render
37938      * render element to dom or tree
37939      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
37940      */
37941     
37942     render : function(el)
37943     {
37944         
37945         el = el || false;
37946         var hp = this.parent ? 1 : 0;
37947         
37948         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
37949             // if parent is a '#.....' string, then let's use that..
37950             var ename = this.parent.substr(1)
37951             this.parent = false;
37952             el = Roo.get(ename);
37953             if (!el) {
37954                 Roo.log("Warning - element can not be found :#" + ename );
37955                 return;
37956             }
37957         }
37958         
37959         
37960         if (!this.parent) {
37961             
37962             el = el ? Roo.get(el) : false;
37963             
37964             // it's a top level one..
37965             this.parent =  {
37966                 el : new Roo.BorderLayout(el || document.body, {
37967                 
37968                      center: {
37969                          titlebar: false,
37970                          autoScroll:false,
37971                          closeOnTab: true,
37972                          tabPosition: 'top',
37973                           //resizeTabs: true,
37974                          alwaysShowTabs: el && hp? false :  true,
37975                          hideTabs: el || !hp ? true :  false,
37976                          minTabWidth: 140
37977                      }
37978                  })
37979             }
37980         }
37981         
37982         
37983             
37984         var tree = this.tree();
37985         tree.region = tree.region || this.region;
37986         this.el = this.parent.el.addxtype(tree);
37987         this.fireEvent('built', this);
37988         
37989         this.panel = this.el;
37990         this.layout = this.panel.layout;    
37991          
37992     }
37993     
37994 });
37995
37996 Roo.apply(Roo.XComponent, {
37997     
37998     /**
37999      * @property  buildCompleted
38000      * True when the builder has completed building the interface.
38001      * @type Boolean
38002      */
38003     buildCompleted : false,
38004      
38005     /**
38006      * @property  topModule
38007      * the upper most module - uses document.element as it's constructor.
38008      * @type Object
38009      */
38010      
38011     topModule  : false,
38012       
38013     /**
38014      * @property  modules
38015      * array of modules to be created by registration system.
38016      * @type {Array} of Roo.XComponent
38017      */
38018     
38019     modules : [],
38020     /**
38021      * @property  elmodules
38022      * array of modules to be created by which use #ID 
38023      * @type {Array} of Roo.XComponent
38024      */
38025      
38026     elmodules : [],
38027
38028     
38029     /**
38030      * Register components to be built later.
38031      *
38032      * This solves the following issues
38033      * - Building is not done on page load, but after an authentication process has occured.
38034      * - Interface elements are registered on page load
38035      * - Parent Interface elements may not be loaded before child, so this handles that..
38036      * 
38037      *
38038      * example:
38039      * 
38040      * MyApp.register({
38041           order : '000001',
38042           module : 'Pman.Tab.projectMgr',
38043           region : 'center',
38044           parent : 'Pman.layout',
38045           disabled : false,  // or use a function..
38046         })
38047      
38048      * * @param {Object} details about module
38049      */
38050     register : function(obj) {
38051         this.modules.push(obj);
38052          
38053     },
38054     /**
38055      * convert a string to an object..
38056      * eg. 'AAA.BBB' -> finds AAA.BBB
38057
38058      */
38059     
38060     toObject : function(str)
38061     {
38062         if (!str || typeof(str) == 'object') {
38063             return str;
38064         }
38065         if (str.substring(0,1) == '#') {
38066             return str;
38067         }
38068
38069         var ar = str.split('.');
38070         var rt, o;
38071         rt = ar.shift();
38072             /** eval:var:o */
38073         try {
38074             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
38075         } catch (e) {
38076             throw "Module not found : " + str;
38077         }
38078         
38079         if (o === false) {
38080             throw "Module not found : " + str;
38081         }
38082         Roo.each(ar, function(e) {
38083             if (typeof(o[e]) == 'undefined') {
38084                 throw "Module not found : " + str;
38085             }
38086             o = o[e];
38087         });
38088         
38089         return o;
38090         
38091     },
38092     
38093     
38094     /**
38095      * move modules into their correct place in the tree..
38096      * 
38097      */
38098     preBuild : function ()
38099     {
38100         var _t = this;
38101         Roo.each(this.modules , function (obj)
38102         {
38103             var opar = obj.parent;
38104             try { 
38105                 obj.parent = this.toObject(opar);
38106             } catch(e) {
38107                 Roo.log(e.toString());
38108                 return;
38109             }
38110             
38111             if (!obj.parent) {
38112                 this.topModule = obj;
38113                 return;
38114             }
38115             if (typeof(obj.parent) == 'string') {
38116                 this.elmodules.push(obj);
38117                 return;
38118             }
38119             if (obj.parent.constructor != Roo.XComponent) {
38120                 Roo.log("Object Parent is not instance of XComponent:" + obj.name)
38121             }
38122             if (!obj.parent.modules) {
38123                 obj.parent.modules = new Roo.util.MixedCollection(false, 
38124                     function(o) { return o.order + '' }
38125                 );
38126             }
38127             
38128             obj.parent.modules.add(obj);
38129         }, this);
38130     },
38131     
38132      /**
38133      * make a list of modules to build.
38134      * @return {Array} list of modules. 
38135      */ 
38136     
38137     buildOrder : function()
38138     {
38139         var _this = this;
38140         var cmp = function(a,b) {   
38141             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
38142         };
38143         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
38144             throw "No top level modules to build";
38145         }
38146         
38147         // make a flat list in order of modules to build.
38148         var mods = this.topModule ? [ this.topModule ] : [];
38149         Roo.each(this.elmodules,function(e) { mods.push(e) });
38150
38151         
38152         // add modules to their parents..
38153         var addMod = function(m) {
38154            // Roo.debug && Roo.log(m.modKey);
38155             
38156             mods.push(m);
38157             if (m.modules) {
38158                 m.modules.keySort('ASC',  cmp );
38159                 m.modules.each(addMod);
38160             }
38161             // not sure if this is used any more..
38162             if (m.finalize) {
38163                 m.finalize.name = m.name + " (clean up) ";
38164                 mods.push(m.finalize);
38165             }
38166             
38167         }
38168         if (this.topModule) { 
38169             this.topModule.modules.keySort('ASC',  cmp );
38170             this.topModule.modules.each(addMod);
38171         }
38172         return mods;
38173     },
38174     
38175      /**
38176      * Build the registered modules.
38177      * @param {Object} parent element.
38178      * @param {Function} optional method to call after module has been added.
38179      * 
38180      */ 
38181    
38182     build : function() 
38183     {
38184         
38185         this.preBuild();
38186         var mods = this.buildOrder();
38187       
38188         //this.allmods = mods;
38189         //Roo.debug && Roo.log(mods);
38190         //return;
38191         if (!mods.length) { // should not happen
38192             throw "NO modules!!!";
38193         }
38194         
38195         
38196         
38197         // flash it up as modal - so we store the mask!?
38198         Roo.MessageBox.show({ title: 'loading' });
38199         Roo.MessageBox.show({
38200            title: "Please wait...",
38201            msg: "Building Interface...",
38202            width:450,
38203            progress:true,
38204            closable:false,
38205            modal: false
38206           
38207         });
38208         var total = mods.length;
38209         
38210         var _this = this;
38211         var progressRun = function() {
38212             if (!mods.length) {
38213                 Roo.debug && Roo.log('hide?');
38214                 Roo.MessageBox.hide();
38215                 if (_this.topModule) { 
38216                     _this.topModule.fireEvent('buildcomplete', _this.topModule);
38217                 }
38218                 // THE END...
38219                 return false;   
38220             }
38221             
38222             var m = mods.shift();
38223             
38224             
38225             Roo.debug && Roo.log(m);
38226             // not sure if this is supported any more.. - modules that are are just function
38227             if (typeof(m) == 'function') { 
38228                 m.call(this);
38229                 return progressRun.defer(10, _this);
38230             } 
38231             
38232             
38233             
38234             Roo.MessageBox.updateProgress(
38235                 (total  - mods.length)/total,  "Building Interface " + (total  - mods.length) + 
38236                     " of " + total + 
38237                     (m.name ? (' - ' + m.name) : '')
38238                     );
38239             
38240          
38241             // is the module disabled?
38242             var disabled = (typeof(m.disabled) == 'function') ?
38243                 m.disabled.call(m.module.disabled) : m.disabled;    
38244             
38245             
38246             if (disabled) {
38247                 return progressRun(); // we do not update the display!
38248             }
38249             
38250             // now build 
38251             
38252             m.render();
38253             // it's 10 on top level, and 1 on others??? why...
38254             return progressRun.defer(10, _this);
38255              
38256         }
38257         progressRun.defer(1, _this);
38258      
38259         
38260         
38261     }
38262     
38263      
38264    
38265     
38266     
38267 });
38268  //<script type="text/javascript">
38269
38270
38271 /**
38272  * @class Roo.Login
38273  * @extends Roo.LayoutDialog
38274  * A generic Login Dialog..... - only one needed in theory!?!?
38275  *
38276  * Fires XComponent builder on success...
38277  * 
38278  * Sends 
38279  *    username,password, lang = for login actions.
38280  *    check = 1 for periodic checking that sesion is valid.
38281  *    passwordRequest = email request password
38282  *    logout = 1 = to logout
38283  * 
38284  * Affects: (this id="????" elements)
38285  *   loading  (removed) (used to indicate application is loading)
38286  *   loading-mask (hides) (used to hide application when it's building loading)
38287  *   
38288  * 
38289  * Usage: 
38290  *    
38291  * 
38292  * Myapp.login = Roo.Login({
38293      url: xxxx,
38294    
38295      realm : 'Myapp', 
38296      
38297      
38298      method : 'POST',
38299      
38300      
38301      * 
38302  })
38303  * 
38304  * 
38305  * 
38306  **/
38307  
38308 Roo.Login = function(cfg)
38309 {
38310     this.addEvents({
38311         'refreshed' : true
38312     });
38313     
38314     Roo.apply(this,cfg);
38315     
38316     Roo.onReady(function() {
38317         this.onLoad();
38318     }, this);
38319     // call parent..
38320     
38321    
38322     Roo.Login.superclass.constructor.call(this, this);
38323     //this.addxtype(this.items[0]);
38324     
38325     
38326 }
38327
38328
38329 Roo.extend(Roo.Login, Roo.LayoutDialog, {
38330     
38331     /**
38332      * @cfg {String} method
38333      * Method used to query for login details.
38334      */
38335     
38336     method : 'POST',
38337     /**
38338      * @cfg {String} url
38339      * URL to query login data. - eg. baseURL + '/Login.php'
38340      */
38341     url : '',
38342     
38343     /**
38344      * @property user
38345      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
38346      * @type {Object} 
38347      */
38348     user : false,
38349     /**
38350      * @property checkFails
38351      * Number of times we have attempted to get authentication check, and failed.
38352      * @type {Number} 
38353      */
38354     checkFails : 0,
38355       /**
38356      * @property intervalID
38357      * The window interval that does the constant login checking.
38358      * @type {Number} 
38359      */
38360     intervalID : 0,
38361     
38362     
38363     onLoad : function() // called on page load...
38364     {
38365         // load 
38366          
38367         if (Roo.get('loading')) { // clear any loading indicator..
38368             Roo.get('loading').remove();
38369         }
38370         
38371         //this.switchLang('en'); // set the language to english..
38372        
38373         this.check({
38374             success:  function(response, opts)  {  // check successfull...
38375             
38376                 var res = this.processResponse(response);
38377                 this.checkFails =0;
38378                 if (!res.success) { // error!
38379                     this.checkFails = 5;
38380                     //console.log('call failure');
38381                     return this.failure(response,opts);
38382                 }
38383                 
38384                 if (!res.data.id) { // id=0 == login failure.
38385                     return this.show();
38386                 }
38387                 
38388                               
38389                         //console.log(success);
38390                 this.fillAuth(res.data);   
38391                 this.checkFails =0;
38392                 Roo.XComponent.build();
38393             },
38394             failure : this.show
38395         });
38396         
38397     }, 
38398     
38399     
38400     check: function(cfg) // called every so often to refresh cookie etc..
38401     {
38402         if (cfg.again) { // could be undefined..
38403             this.checkFails++;
38404         } else {
38405             this.checkFails = 0;
38406         }
38407         var _this = this;
38408         if (this.sending) {
38409             if ( this.checkFails > 4) {
38410                 Roo.MessageBox.alert("Error",  
38411                     "Error getting authentication status. - try reloading, or wait a while", function() {
38412                         _this.sending = false;
38413                     }); 
38414                 return;
38415             }
38416             cfg.again = true;
38417             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
38418             return;
38419         }
38420         this.sending = true;
38421         
38422         Roo.Ajax.request({  
38423             url: this.url,
38424             params: {
38425                 getAuthUser: true
38426             },  
38427             method: this.method,
38428             success:  cfg.success || this.success,
38429             failure : cfg.failure || this.failure,
38430             scope : this,
38431             callCfg : cfg
38432               
38433         });  
38434     }, 
38435     
38436     
38437     logout: function()
38438     {
38439         window.onbeforeunload = function() { }; // false does not work for IE..
38440         this.user = false;
38441         var _this = this;
38442         
38443         Roo.Ajax.request({  
38444             url: this.url,
38445             params: {
38446                 logout: 1
38447             },  
38448             method: 'GET',
38449             failure : function() {
38450                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
38451                     document.location = document.location.toString() + '?ts=' + Math.random();
38452                 });
38453                 
38454             },
38455             success : function() {
38456                 _this.user = false;
38457                 this.checkFails =0;
38458                 // fixme..
38459                 document.location = document.location.toString() + '?ts=' + Math.random();
38460             }
38461               
38462               
38463         }); 
38464     },
38465     
38466     processResponse : function (response)
38467     {
38468         var res = '';
38469         try {
38470             res = Roo.decode(response.responseText);
38471             // oops...
38472             if (typeof(res) != 'object') {
38473                 res = { success : false, errorMsg : res, errors : true };
38474             }
38475             if (typeof(res.success) == 'undefined') {
38476                 res.success = false;
38477             }
38478             
38479         } catch(e) {
38480             res = { success : false,  errorMsg : response.responseText, errors : true };
38481         }
38482         return res;
38483     },
38484     
38485     success : function(response, opts)  // check successfull...
38486     {  
38487         this.sending = false;
38488         var res = this.processResponse(response);
38489         if (!res.success) {
38490             return this.failure(response, opts);
38491         }
38492         if (!res.data || !res.data.id) {
38493             return this.failure(response,opts);
38494         }
38495         //console.log(res);
38496         this.fillAuth(res.data);
38497         
38498         this.checkFails =0;
38499         
38500     },
38501     
38502     
38503     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
38504     {
38505         this.authUser = -1;
38506         this.sending = false;
38507         var res = this.processResponse(response);
38508         //console.log(res);
38509         if ( this.checkFails > 2) {
38510         
38511             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
38512                 "Error getting authentication status. - try reloading"); 
38513             return;
38514         }
38515         opts.callCfg.again = true;
38516         this.check.defer(1000, this, [ opts.callCfg ]);
38517         return;  
38518     },
38519     
38520     
38521     
38522     fillAuth: function(au) {
38523         this.startAuthCheck();
38524         this.authUserId = au.id;
38525         this.authUser = au;
38526         this.lastChecked = new Date();
38527         this.fireEvent('refreshed', au);
38528         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
38529         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
38530         au.lang = au.lang || 'en';
38531         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
38532         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
38533         this.switchLang(au.lang );
38534         
38535      
38536         // open system... - -on setyp..
38537         if (this.authUserId  < 0) {
38538             Roo.MessageBox.alert("Warning", 
38539                 "This is an open system - please set up a admin user with a password.");  
38540         }
38541          
38542         //Pman.onload(); // which should do nothing if it's a re-auth result...
38543         
38544              
38545     },
38546     
38547     startAuthCheck : function() // starter for timeout checking..
38548     {
38549         if (this.intervalID) { // timer already in place...
38550             return false;
38551         }
38552         var _this = this;
38553         this.intervalID =  window.setInterval(function() {
38554               _this.check(false);
38555             }, 120000); // every 120 secs = 2mins..
38556         
38557         
38558     },
38559          
38560     
38561     switchLang : function (lang) 
38562     {
38563         _T = typeof(_T) == 'undefined' ? false : _T;
38564           if (!_T || !lang.length) {
38565             return;
38566         }
38567         
38568         if (!_T && lang != 'en') {
38569             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
38570             return;
38571         }
38572         
38573         if (typeof(_T.en) == 'undefined') {
38574             _T.en = {};
38575             Roo.apply(_T.en, _T);
38576         }
38577         
38578         if (typeof(_T[lang]) == 'undefined') {
38579             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
38580             return;
38581         }
38582         
38583         
38584         Roo.apply(_T, _T[lang]);
38585         // just need to set the text values for everything...
38586         var _this = this;
38587         /* this will not work ...
38588         if (this.form) { 
38589             
38590                
38591             function formLabel(name, val) {
38592                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
38593             }
38594             
38595             formLabel('password', "Password"+':');
38596             formLabel('username', "Email Address"+':');
38597             formLabel('lang', "Language"+':');
38598             this.dialog.setTitle("Login");
38599             this.dialog.buttons[0].setText("Forgot Password");
38600             this.dialog.buttons[1].setText("Login");
38601         }
38602         */
38603         
38604         
38605     },
38606     
38607     
38608     title: "Login",
38609     modal: true,
38610     width:  350,
38611     //height: 230,
38612     height: 180,
38613     shadow: true,
38614     minWidth:200,
38615     minHeight:180,
38616     //proxyDrag: true,
38617     closable: false,
38618     draggable: false,
38619     collapsible: false,
38620     resizable: false,
38621     center: {  // needed??
38622         autoScroll:false,
38623         titlebar: false,
38624        // tabPosition: 'top',
38625         hideTabs: true,
38626         closeOnTab: true,
38627         alwaysShowTabs: false
38628     } ,
38629     listeners : {
38630         
38631         show  : function(dlg)
38632         {
38633             //console.log(this);
38634             this.form = this.layout.getRegion('center').activePanel.form;
38635             this.form.dialog = dlg;
38636             this.buttons[0].form = this.form;
38637             this.buttons[0].dialog = dlg;
38638             this.buttons[1].form = this.form;
38639             this.buttons[1].dialog = dlg;
38640            
38641            //this.resizeToLogo.defer(1000,this);
38642             // this is all related to resizing for logos..
38643             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
38644            //// if (!sz) {
38645              //   this.resizeToLogo.defer(1000,this);
38646              //   return;
38647            // }
38648             //var w = Ext.lib.Dom.getViewWidth() - 100;
38649             //var h = Ext.lib.Dom.getViewHeight() - 100;
38650             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
38651             //this.center();
38652             if (this.disabled) {
38653                 this.hide();
38654                 return;
38655             }
38656             
38657             if (this.user.id < 0) { // used for inital setup situations.
38658                 return;
38659             }
38660             
38661             if (this.intervalID) {
38662                 // remove the timer
38663                 window.clearInterval(this.intervalID);
38664                 this.intervalID = false;
38665             }
38666             
38667             
38668             if (Roo.get('loading')) {
38669                 Roo.get('loading').remove();
38670             }
38671             if (Roo.get('loading-mask')) {
38672                 Roo.get('loading-mask').hide();
38673             }
38674             
38675             //incomming._node = tnode;
38676             this.form.reset();
38677             //this.dialog.modal = !modal;
38678             //this.dialog.show();
38679             this.el.unmask(); 
38680             
38681             
38682             this.form.setValues({
38683                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
38684                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
38685             });
38686             
38687             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
38688             if (this.form.findField('username').getValue().length > 0 ){
38689                 this.form.findField('password').focus();
38690             } else {
38691                this.form.findField('username').focus();
38692             }
38693     
38694         }
38695     },
38696     items : [
38697          {
38698        
38699             xtype : 'ContentPanel',
38700             xns : Roo,
38701             region: 'center',
38702             fitToFrame : true,
38703             
38704             items : [
38705     
38706                 {
38707                
38708                     xtype : 'Form',
38709                     xns : Roo.form,
38710                     labelWidth: 100,
38711                     style : 'margin: 10px;',
38712                     
38713                     listeners : {
38714                         actionfailed : function(f, act) {
38715                             // form can return { errors: .... }
38716                                 
38717                             //act.result.errors // invalid form element list...
38718                             //act.result.errorMsg// invalid form element list...
38719                             
38720                             this.dialog.el.unmask();
38721                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
38722                                         "Login failed - communication error - try again.");
38723                                       
38724                         },
38725                         actioncomplete: function(re, act) {
38726                              
38727                             Roo.state.Manager.set(
38728                                 this.dialog.realm + '.username',  
38729                                     this.findField('username').getValue()
38730                             );
38731                             Roo.state.Manager.set(
38732                                 this.dialog.realm + '.lang',  
38733                                 this.findField('lang').getValue() 
38734                             );
38735                             
38736                             this.dialog.fillAuth(act.result.data);
38737                               
38738                             this.dialog.hide();
38739                             
38740                             if (Roo.get('loading-mask')) {
38741                                 Roo.get('loading-mask').show();
38742                             }
38743                             Roo.XComponent.build();
38744                             
38745                              
38746                             
38747                         }
38748                     },
38749                     items : [
38750                         {
38751                             xtype : 'TextField',
38752                             xns : Roo.form,
38753                             fieldLabel: "Email Address",
38754                             name: 'username',
38755                             width:200,
38756                             autoCreate : {tag: "input", type: "text", size: "20"}
38757                         },
38758                         {
38759                             xtype : 'TextField',
38760                             xns : Roo.form,
38761                             fieldLabel: "Password",
38762                             inputType: 'password',
38763                             name: 'password',
38764                             width:200,
38765                             autoCreate : {tag: "input", type: "text", size: "20"},
38766                             listeners : {
38767                                 specialkey : function(e,ev) {
38768                                     if (ev.keyCode == 13) {
38769                                         this.form.dialog.el.mask("Logging in");
38770                                         this.form.doAction('submit', {
38771                                             url: this.form.dialog.url,
38772                                             method: this.form.dialog.method
38773                                         });
38774                                     }
38775                                 }
38776                             }  
38777                         },
38778                         {
38779                             xtype : 'ComboBox',
38780                             xns : Roo.form,
38781                             fieldLabel: "Language",
38782                             name : 'langdisp',
38783                             store: {
38784                                 xtype : 'SimpleStore',
38785                                 fields: ['lang', 'ldisp'],
38786                                 data : [
38787                                     [ 'en', 'English' ],
38788                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
38789                                     [ 'zh_CN', '\u7C21\u4E2D' ]
38790                                 ]
38791                             },
38792                             
38793                             valueField : 'lang',
38794                             hiddenName:  'lang',
38795                             width: 200,
38796                             displayField:'ldisp',
38797                             typeAhead: false,
38798                             editable: false,
38799                             mode: 'local',
38800                             triggerAction: 'all',
38801                             emptyText:'Select a Language...',
38802                             selectOnFocus:true,
38803                             listeners : {
38804                                 select :  function(cb, rec, ix) {
38805                                     this.form.switchLang(rec.data.lang);
38806                                 }
38807                             }
38808                         
38809                         }
38810                     ]
38811                 }
38812                   
38813                 
38814             ]
38815         }
38816     ],
38817     buttons : [
38818         {
38819             xtype : 'Button',
38820             xns : 'Roo',
38821             text : "Forgot Password",
38822             listeners : {
38823                 click : function() {
38824                     //console.log(this);
38825                     var n = this.form.findField('username').getValue();
38826                     if (!n.length) {
38827                         Roo.MessageBox.alert("Error", "Fill in your email address");
38828                         return;
38829                     }
38830                     Roo.Ajax.request({
38831                         url: this.dialog.url,
38832                         params: {
38833                             passwordRequest: n
38834                         },
38835                         method: this.dialog.method,
38836                         success:  function(response, opts)  {  // check successfull...
38837                         
38838                             var res = this.dialog.processResponse(response);
38839                             if (!res.success) { // error!
38840                                Roo.MessageBox.alert("Error" ,
38841                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
38842                                return;
38843                             }
38844                             Roo.MessageBox.alert("Notice" ,
38845                                 "Please check you email for the Password Reset message");
38846                         },
38847                         failure : function() {
38848                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
38849                         }
38850                         
38851                     });
38852                 }
38853             }
38854         },
38855         {
38856             xtype : 'Button',
38857             xns : 'Roo',
38858             text : "Login",
38859             listeners : {
38860                 
38861                 click : function () {
38862                         
38863                     this.dialog.el.mask("Logging in");
38864                     this.form.doAction('submit', {
38865                             url: this.dialog.url,
38866                             method: this.dialog.method
38867                     });
38868                 }
38869             }
38870         }
38871     ]
38872   
38873   
38874 })
38875  
38876
38877
38878