commit
[roojs1] / roojs-ui-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12
13
14 /*
15  * These classes are derivatives of the similarly named classes in the YUI Library.
16  * The original license:
17  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
18  * Code licensed under the BSD License:
19  * http://developer.yahoo.net/yui/license.txt
20  */
21
22 (function() {
23
24 var Event=Roo.EventManager;
25 var Dom=Roo.lib.Dom;
26
27 /**
28  * @class Roo.dd.DragDrop
29  * @extends Roo.util.Observable
30  * Defines the interface and base operation of items that that can be
31  * dragged or can be drop targets.  It was designed to be extended, overriding
32  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
33  * Up to three html elements can be associated with a DragDrop instance:
34  * <ul>
35  * <li>linked element: the element that is passed into the constructor.
36  * This is the element which defines the boundaries for interaction with
37  * other DragDrop objects.</li>
38  * <li>handle element(s): The drag operation only occurs if the element that
39  * was clicked matches a handle element.  By default this is the linked
40  * element, but there are times that you will want only a portion of the
41  * linked element to initiate the drag operation, and the setHandleElId()
42  * method provides a way to define this.</li>
43  * <li>drag element: this represents the element that would be moved along
44  * with the cursor during a drag operation.  By default, this is the linked
45  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
46  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
47  * </li>
48  * </ul>
49  * This class should not be instantiated until the onload event to ensure that
50  * the associated elements are available.
51  * The following would define a DragDrop obj that would interact with any
52  * other DragDrop obj in the "group1" group:
53  * <pre>
54  *  dd = new Roo.dd.DragDrop("div1", "group1");
55  * </pre>
56  * Since none of the event handlers have been implemented, nothing would
57  * actually happen if you were to run the code above.  Normally you would
58  * override this class or one of the default implementations, but you can
59  * also override the methods you want on an instance of the class...
60  * <pre>
61  *  dd.onDragDrop = function(e, id) {
62  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
63  *  }
64  * </pre>
65  * @constructor
66  * @param {String} id of the element that is linked to this instance
67  * @param {String} sGroup the group of related DragDrop objects
68  * @param {object} config an object containing configurable attributes
69  *                Valid properties for DragDrop:
70  *                    padding, isTarget, maintainOffset, primaryButtonOnly
71  */
72 Roo.dd.DragDrop = function(id, sGroup, config) {
73     if (id) {
74         this.init(id, sGroup, config);
75     }
76     
77 };
78
79 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
80
81     /**
82      * The id of the element associated with this object.  This is what we
83      * refer to as the "linked element" because the size and position of
84      * this element is used to determine when the drag and drop objects have
85      * interacted.
86      * @property id
87      * @type String
88      */
89     id: null,
90
91     /**
92      * Configuration attributes passed into the constructor
93      * @property config
94      * @type object
95      */
96     config: null,
97
98     /**
99      * The id of the element that will be dragged.  By default this is same
100      * as the linked element , but could be changed to another element. Ex:
101      * Roo.dd.DDProxy
102      * @property dragElId
103      * @type String
104      * @private
105      */
106     dragElId: null,
107
108     /**
109      * the id of the element that initiates the drag operation.  By default
110      * this is the linked element, but could be changed to be a child of this
111      * element.  This lets us do things like only starting the drag when the
112      * header element within the linked html element is clicked.
113      * @property handleElId
114      * @type String
115      * @private
116      */
117     handleElId: null,
118
119     /**
120      * An associative array of HTML tags that will be ignored if clicked.
121      * @property invalidHandleTypes
122      * @type {string: string}
123      */
124     invalidHandleTypes: null,
125
126     /**
127      * An associative array of ids for elements that will be ignored if clicked
128      * @property invalidHandleIds
129      * @type {string: string}
130      */
131     invalidHandleIds: null,
132
133     /**
134      * An indexted array of css class names for elements that will be ignored
135      * if clicked.
136      * @property invalidHandleClasses
137      * @type string[]
138      */
139     invalidHandleClasses: null,
140
141     /**
142      * The linked element's absolute X position at the time the drag was
143      * started
144      * @property startPageX
145      * @type int
146      * @private
147      */
148     startPageX: 0,
149
150     /**
151      * The linked element's absolute X position at the time the drag was
152      * started
153      * @property startPageY
154      * @type int
155      * @private
156      */
157     startPageY: 0,
158
159     /**
160      * The group defines a logical collection of DragDrop objects that are
161      * related.  Instances only get events when interacting with other
162      * DragDrop object in the same group.  This lets us define multiple
163      * groups using a single DragDrop subclass if we want.
164      * @property groups
165      * @type {string: string}
166      */
167     groups: null,
168
169     /**
170      * Individual drag/drop instances can be locked.  This will prevent
171      * onmousedown start drag.
172      * @property locked
173      * @type boolean
174      * @private
175      */
176     locked: false,
177
178     /**
179      * Lock this instance
180      * @method lock
181      */
182     lock: function() { this.locked = true; },
183
184     /**
185      * Unlock this instace
186      * @method unlock
187      */
188     unlock: function() { this.locked = false; },
189
190     /**
191      * By default, all insances can be a drop target.  This can be disabled by
192      * setting isTarget to false.
193      * @method isTarget
194      * @type boolean
195      */
196     isTarget: true,
197
198     /**
199      * The padding configured for this drag and drop object for calculating
200      * the drop zone intersection with this object.
201      * @method padding
202      * @type int[]
203      */
204     padding: null,
205
206     /**
207      * Cached reference to the linked element
208      * @property _domRef
209      * @private
210      */
211     _domRef: null,
212
213     /**
214      * Internal typeof flag
215      * @property __ygDragDrop
216      * @private
217      */
218     __ygDragDrop: true,
219
220     /**
221      * Set to true when horizontal contraints are applied
222      * @property constrainX
223      * @type boolean
224      * @private
225      */
226     constrainX: false,
227
228     /**
229      * Set to true when vertical contraints are applied
230      * @property constrainY
231      * @type boolean
232      * @private
233      */
234     constrainY: false,
235
236     /**
237      * The left constraint
238      * @property minX
239      * @type int
240      * @private
241      */
242     minX: 0,
243
244     /**
245      * The right constraint
246      * @property maxX
247      * @type int
248      * @private
249      */
250     maxX: 0,
251
252     /**
253      * The up constraint
254      * @property minY
255      * @type int
256      * @type int
257      * @private
258      */
259     minY: 0,
260
261     /**
262      * The down constraint
263      * @property maxY
264      * @type int
265      * @private
266      */
267     maxY: 0,
268
269     /**
270      * Maintain offsets when we resetconstraints.  Set to true when you want
271      * the position of the element relative to its parent to stay the same
272      * when the page changes
273      *
274      * @property maintainOffset
275      * @type boolean
276      */
277     maintainOffset: false,
278
279     /**
280      * Array of pixel locations the element will snap to if we specified a
281      * horizontal graduation/interval.  This array is generated automatically
282      * when you define a tick interval.
283      * @property xTicks
284      * @type int[]
285      */
286     xTicks: null,
287
288     /**
289      * Array of pixel locations the element will snap to if we specified a
290      * vertical graduation/interval.  This array is generated automatically
291      * when you define a tick interval.
292      * @property yTicks
293      * @type int[]
294      */
295     yTicks: null,
296
297     /**
298      * By default the drag and drop instance will only respond to the primary
299      * button click (left button for a right-handed mouse).  Set to true to
300      * allow drag and drop to start with any mouse click that is propogated
301      * by the browser
302      * @property primaryButtonOnly
303      * @type boolean
304      */
305     primaryButtonOnly: true,
306
307     /**
308      * The availabe property is false until the linked dom element is accessible.
309      * @property available
310      * @type boolean
311      */
312     available: false,
313
314     /**
315      * By default, drags can only be initiated if the mousedown occurs in the
316      * region the linked element is.  This is done in part to work around a
317      * bug in some browsers that mis-report the mousedown if the previous
318      * mouseup happened outside of the window.  This property is set to true
319      * if outer handles are defined.
320      *
321      * @property hasOuterHandles
322      * @type boolean
323      * @default false
324      */
325     hasOuterHandles: false,
326
327     /**
328      * Code that executes immediately before the startDrag event
329      * @method b4StartDrag
330      * @private
331      */
332     b4StartDrag: function(x, y) { },
333
334     /**
335      * Abstract method called after a drag/drop object is clicked
336      * and the drag or mousedown time thresholds have beeen met.
337      * @method startDrag
338      * @param {int} X click location
339      * @param {int} Y click location
340      */
341     startDrag: function(x, y) { /* override this */ },
342
343     /**
344      * Code that executes immediately before the onDrag event
345      * @method b4Drag
346      * @private
347      */
348     b4Drag: function(e) { },
349
350     /**
351      * Abstract method called during the onMouseMove event while dragging an
352      * object.
353      * @method onDrag
354      * @param {Event} e the mousemove event
355      */
356     onDrag: function(e) { /* override this */ },
357
358     /**
359      * Abstract method called when this element fist begins hovering over
360      * another DragDrop obj
361      * @method onDragEnter
362      * @param {Event} e the mousemove event
363      * @param {String|DragDrop[]} id In POINT mode, the element
364      * id this is hovering over.  In INTERSECT mode, an array of one or more
365      * dragdrop items being hovered over.
366      */
367     onDragEnter: function(e, id) { /* override this */ },
368
369     /**
370      * Code that executes immediately before the onDragOver event
371      * @method b4DragOver
372      * @private
373      */
374     b4DragOver: function(e) { },
375
376     /**
377      * Abstract method called when this element is hovering over another
378      * DragDrop obj
379      * @method onDragOver
380      * @param {Event} e the mousemove event
381      * @param {String|DragDrop[]} id In POINT mode, the element
382      * id this is hovering over.  In INTERSECT mode, an array of dd items
383      * being hovered over.
384      */
385     onDragOver: function(e, id) { /* override this */ },
386
387     /**
388      * Code that executes immediately before the onDragOut event
389      * @method b4DragOut
390      * @private
391      */
392     b4DragOut: function(e) { },
393
394     /**
395      * Abstract method called when we are no longer hovering over an element
396      * @method onDragOut
397      * @param {Event} e the mousemove event
398      * @param {String|DragDrop[]} id In POINT mode, the element
399      * id this was hovering over.  In INTERSECT mode, an array of dd items
400      * that the mouse is no longer over.
401      */
402     onDragOut: function(e, id) { /* override this */ },
403
404     /**
405      * Code that executes immediately before the onDragDrop event
406      * @method b4DragDrop
407      * @private
408      */
409     b4DragDrop: function(e) { },
410
411     /**
412      * Abstract method called when this item is dropped on another DragDrop
413      * obj
414      * @method onDragDrop
415      * @param {Event} e the mouseup event
416      * @param {String|DragDrop[]} id In POINT mode, the element
417      * id this was dropped on.  In INTERSECT mode, an array of dd items this
418      * was dropped on.
419      */
420     onDragDrop: function(e, id) { /* override this */ },
421
422     /**
423      * Abstract method called when this item is dropped on an area with no
424      * drop target
425      * @method onInvalidDrop
426      * @param {Event} e the mouseup event
427      */
428     onInvalidDrop: function(e) { /* override this */ },
429
430     /**
431      * Code that executes immediately before the endDrag event
432      * @method b4EndDrag
433      * @private
434      */
435     b4EndDrag: function(e) { },
436
437     /**
438      * Fired when we are done dragging the object
439      * @method endDrag
440      * @param {Event} e the mouseup event
441      */
442     endDrag: function(e) { /* override this */ },
443
444     /**
445      * Code executed immediately before the onMouseDown event
446      * @method b4MouseDown
447      * @param {Event} e the mousedown event
448      * @private
449      */
450     b4MouseDown: function(e) {  },
451
452     /**
453      * Event handler that fires when a drag/drop obj gets a mousedown
454      * @method onMouseDown
455      * @param {Event} e the mousedown event
456      */
457     onMouseDown: function(e) { /* override this */ },
458
459     /**
460      * Event handler that fires when a drag/drop obj gets a mouseup
461      * @method onMouseUp
462      * @param {Event} e the mouseup event
463      */
464     onMouseUp: function(e) { /* override this */ },
465
466     /**
467      * Override the onAvailable method to do what is needed after the initial
468      * position was determined.
469      * @method onAvailable
470      */
471     onAvailable: function () {
472     },
473
474     /*
475      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
476      * @type Object
477      */
478     defaultPadding : {left:0, right:0, top:0, bottom:0},
479
480     /*
481      * Initializes the drag drop object's constraints to restrict movement to a certain element.
482  *
483  * Usage:
484  <pre><code>
485  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
486                 { dragElId: "existingProxyDiv" });
487  dd.startDrag = function(){
488      this.constrainTo("parent-id");
489  };
490  </code></pre>
491  * Or you can initalize it using the {@link Roo.Element} object:
492  <pre><code>
493  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
494      startDrag : function(){
495          this.constrainTo("parent-id");
496      }
497  });
498  </code></pre>
499      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
500      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
501      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
502      * an object containing the sides to pad. For example: {right:10, bottom:10}
503      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
504      */
505     constrainTo : function(constrainTo, pad, inContent){
506         if(typeof pad == "number"){
507             pad = {left: pad, right:pad, top:pad, bottom:pad};
508         }
509         pad = pad || this.defaultPadding;
510         var b = Roo.get(this.getEl()).getBox();
511         var ce = Roo.get(constrainTo);
512         var s = ce.getScroll();
513         var c, cd = ce.dom;
514         if(cd == document.body){
515             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
516         }else{
517             xy = ce.getXY();
518             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
519         }
520
521
522         var topSpace = b.y - c.y;
523         var leftSpace = b.x - c.x;
524
525         this.resetConstraints();
526         this.setXConstraint(leftSpace - (pad.left||0), // left
527                 c.width - leftSpace - b.width - (pad.right||0) //right
528         );
529         this.setYConstraint(topSpace - (pad.top||0), //top
530                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
531         );
532     },
533
534     /**
535      * Returns a reference to the linked element
536      * @method getEl
537      * @return {HTMLElement} the html element
538      */
539     getEl: function() {
540         if (!this._domRef) {
541             this._domRef = Roo.getDom(this.id);
542         }
543
544         return this._domRef;
545     },
546
547     /**
548      * Returns a reference to the actual element to drag.  By default this is
549      * the same as the html element, but it can be assigned to another
550      * element. An example of this can be found in Roo.dd.DDProxy
551      * @method getDragEl
552      * @return {HTMLElement} the html element
553      */
554     getDragEl: function() {
555         return Roo.getDom(this.dragElId);
556     },
557
558     /**
559      * Sets up the DragDrop object.  Must be called in the constructor of any
560      * Roo.dd.DragDrop subclass
561      * @method init
562      * @param id the id of the linked element
563      * @param {String} sGroup the group of related items
564      * @param {object} config configuration attributes
565      */
566     init: function(id, sGroup, config) {
567         this.initTarget(id, sGroup, config);
568         Event.on(this.id, "mousedown", this.handleMouseDown, this);
569         // Event.on(this.id, "selectstart", Event.preventDefault);
570     },
571
572     /**
573      * Initializes Targeting functionality only... the object does not
574      * get a mousedown handler.
575      * @method initTarget
576      * @param id the id of the linked element
577      * @param {String} sGroup the group of related items
578      * @param {object} config configuration attributes
579      */
580     initTarget: function(id, sGroup, config) {
581
582         // configuration attributes
583         this.config = config || {};
584
585         // create a local reference to the drag and drop manager
586         this.DDM = Roo.dd.DDM;
587         // initialize the groups array
588         this.groups = {};
589
590         // assume that we have an element reference instead of an id if the
591         // parameter is not a string
592         if (typeof id !== "string") {
593             id = Roo.id(id);
594         }
595
596         // set the id
597         this.id = id;
598
599         // add to an interaction group
600         this.addToGroup((sGroup) ? sGroup : "default");
601
602         // We don't want to register this as the handle with the manager
603         // so we just set the id rather than calling the setter.
604         this.handleElId = id;
605
606         // the linked element is the element that gets dragged by default
607         this.setDragElId(id);
608
609         // by default, clicked anchors will not start drag operations.
610         this.invalidHandleTypes = { A: "A" };
611         this.invalidHandleIds = {};
612         this.invalidHandleClasses = [];
613
614         this.applyConfig();
615
616         this.handleOnAvailable();
617     },
618
619     /**
620      * Applies the configuration parameters that were passed into the constructor.
621      * This is supposed to happen at each level through the inheritance chain.  So
622      * a DDProxy implentation will execute apply config on DDProxy, DD, and
623      * DragDrop in order to get all of the parameters that are available in
624      * each object.
625      * @method applyConfig
626      */
627     applyConfig: function() {
628
629         // configurable properties:
630         //    padding, isTarget, maintainOffset, primaryButtonOnly
631         this.padding           = this.config.padding || [0, 0, 0, 0];
632         this.isTarget          = (this.config.isTarget !== false);
633         this.maintainOffset    = (this.config.maintainOffset);
634         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
635
636     },
637
638     /**
639      * Executed when the linked element is available
640      * @method handleOnAvailable
641      * @private
642      */
643     handleOnAvailable: function() {
644         this.available = true;
645         this.resetConstraints();
646         this.onAvailable();
647     },
648
649      /**
650      * Configures the padding for the target zone in px.  Effectively expands
651      * (or reduces) the virtual object size for targeting calculations.
652      * Supports css-style shorthand; if only one parameter is passed, all sides
653      * will have that padding, and if only two are passed, the top and bottom
654      * will have the first param, the left and right the second.
655      * @method setPadding
656      * @param {int} iTop    Top pad
657      * @param {int} iRight  Right pad
658      * @param {int} iBot    Bot pad
659      * @param {int} iLeft   Left pad
660      */
661     setPadding: function(iTop, iRight, iBot, iLeft) {
662         // this.padding = [iLeft, iRight, iTop, iBot];
663         if (!iRight && 0 !== iRight) {
664             this.padding = [iTop, iTop, iTop, iTop];
665         } else if (!iBot && 0 !== iBot) {
666             this.padding = [iTop, iRight, iTop, iRight];
667         } else {
668             this.padding = [iTop, iRight, iBot, iLeft];
669         }
670     },
671
672     /**
673      * Stores the initial placement of the linked element.
674      * @method setInitialPosition
675      * @param {int} diffX   the X offset, default 0
676      * @param {int} diffY   the Y offset, default 0
677      */
678     setInitPosition: function(diffX, diffY) {
679         var el = this.getEl();
680
681         if (!this.DDM.verifyEl(el)) {
682             return;
683         }
684
685         var dx = diffX || 0;
686         var dy = diffY || 0;
687
688         var p = Dom.getXY( el );
689
690         this.initPageX = p[0] - dx;
691         this.initPageY = p[1] - dy;
692
693         this.lastPageX = p[0];
694         this.lastPageY = p[1];
695
696
697         this.setStartPosition(p);
698     },
699
700     /**
701      * Sets the start position of the element.  This is set when the obj
702      * is initialized, the reset when a drag is started.
703      * @method setStartPosition
704      * @param pos current position (from previous lookup)
705      * @private
706      */
707     setStartPosition: function(pos) {
708         var p = pos || Dom.getXY( this.getEl() );
709         this.deltaSetXY = null;
710
711         this.startPageX = p[0];
712         this.startPageY = p[1];
713     },
714
715     /**
716      * Add this instance to a group of related drag/drop objects.  All
717      * instances belong to at least one group, and can belong to as many
718      * groups as needed.
719      * @method addToGroup
720      * @param sGroup {string} the name of the group
721      */
722     addToGroup: function(sGroup) {
723         this.groups[sGroup] = true;
724         this.DDM.regDragDrop(this, sGroup);
725     },
726
727     /**
728      * Remove's this instance from the supplied interaction group
729      * @method removeFromGroup
730      * @param {string}  sGroup  The group to drop
731      */
732     removeFromGroup: function(sGroup) {
733         if (this.groups[sGroup]) {
734             delete this.groups[sGroup];
735         }
736
737         this.DDM.removeDDFromGroup(this, sGroup);
738     },
739
740     /**
741      * Allows you to specify that an element other than the linked element
742      * will be moved with the cursor during a drag
743      * @method setDragElId
744      * @param id {string} the id of the element that will be used to initiate the drag
745      */
746     setDragElId: function(id) {
747         this.dragElId = id;
748     },
749
750     /**
751      * Allows you to specify a child of the linked element that should be
752      * used to initiate the drag operation.  An example of this would be if
753      * you have a content div with text and links.  Clicking anywhere in the
754      * content area would normally start the drag operation.  Use this method
755      * to specify that an element inside of the content div is the element
756      * that starts the drag operation.
757      * @method setHandleElId
758      * @param id {string} the id of the element that will be used to
759      * initiate the drag.
760      */
761     setHandleElId: function(id) {
762         if (typeof id !== "string") {
763             id = Roo.id(id);
764         }
765         this.handleElId = id;
766         this.DDM.regHandle(this.id, id);
767     },
768
769     /**
770      * Allows you to set an element outside of the linked element as a drag
771      * handle
772      * @method setOuterHandleElId
773      * @param id the id of the element that will be used to initiate the drag
774      */
775     setOuterHandleElId: function(id) {
776         if (typeof id !== "string") {
777             id = Roo.id(id);
778         }
779         Event.on(id, "mousedown",
780                 this.handleMouseDown, this);
781         this.setHandleElId(id);
782
783         this.hasOuterHandles = true;
784     },
785
786     /**
787      * Remove all drag and drop hooks for this element
788      * @method unreg
789      */
790     unreg: function() {
791         Event.un(this.id, "mousedown",
792                 this.handleMouseDown);
793         this._domRef = null;
794         this.DDM._remove(this);
795     },
796
797     destroy : function(){
798         this.unreg();
799     },
800
801     /**
802      * Returns true if this instance is locked, or the drag drop mgr is locked
803      * (meaning that all drag/drop is disabled on the page.)
804      * @method isLocked
805      * @return {boolean} true if this obj or all drag/drop is locked, else
806      * false
807      */
808     isLocked: function() {
809         return (this.DDM.isLocked() || this.locked);
810     },
811
812     /**
813      * Fired when this object is clicked
814      * @method handleMouseDown
815      * @param {Event} e
816      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
817      * @private
818      */
819     handleMouseDown: function(e, oDD){
820         if (this.primaryButtonOnly && e.button != 0) {
821             return;
822         }
823
824         if (this.isLocked()) {
825             return;
826         }
827
828         this.DDM.refreshCache(this.groups);
829
830         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
831         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
832         } else {
833             if (this.clickValidator(e)) {
834
835                 // set the initial element position
836                 this.setStartPosition();
837
838
839                 this.b4MouseDown(e);
840                 this.onMouseDown(e);
841
842                 this.DDM.handleMouseDown(e, this);
843
844                 this.DDM.stopEvent(e);
845             } else {
846
847
848             }
849         }
850     },
851
852     clickValidator: function(e) {
853         var target = e.getTarget();
854         return ( this.isValidHandleChild(target) &&
855                     (this.id == this.handleElId ||
856                         this.DDM.handleWasClicked(target, this.id)) );
857     },
858
859     /**
860      * Allows you to specify a tag name that should not start a drag operation
861      * when clicked.  This is designed to facilitate embedding links within a
862      * drag handle that do something other than start the drag.
863      * @method addInvalidHandleType
864      * @param {string} tagName the type of element to exclude
865      */
866     addInvalidHandleType: function(tagName) {
867         var type = tagName.toUpperCase();
868         this.invalidHandleTypes[type] = type;
869     },
870
871     /**
872      * Lets you to specify an element id for a child of a drag handle
873      * that should not initiate a drag
874      * @method addInvalidHandleId
875      * @param {string} id the element id of the element you wish to ignore
876      */
877     addInvalidHandleId: function(id) {
878         if (typeof id !== "string") {
879             id = Roo.id(id);
880         }
881         this.invalidHandleIds[id] = id;
882     },
883
884     /**
885      * Lets you specify a css class of elements that will not initiate a drag
886      * @method addInvalidHandleClass
887      * @param {string} cssClass the class of the elements you wish to ignore
888      */
889     addInvalidHandleClass: function(cssClass) {
890         this.invalidHandleClasses.push(cssClass);
891     },
892
893     /**
894      * Unsets an excluded tag name set by addInvalidHandleType
895      * @method removeInvalidHandleType
896      * @param {string} tagName the type of element to unexclude
897      */
898     removeInvalidHandleType: function(tagName) {
899         var type = tagName.toUpperCase();
900         // this.invalidHandleTypes[type] = null;
901         delete this.invalidHandleTypes[type];
902     },
903
904     /**
905      * Unsets an invalid handle id
906      * @method removeInvalidHandleId
907      * @param {string} id the id of the element to re-enable
908      */
909     removeInvalidHandleId: function(id) {
910         if (typeof id !== "string") {
911             id = Roo.id(id);
912         }
913         delete this.invalidHandleIds[id];
914     },
915
916     /**
917      * Unsets an invalid css class
918      * @method removeInvalidHandleClass
919      * @param {string} cssClass the class of the element(s) you wish to
920      * re-enable
921      */
922     removeInvalidHandleClass: function(cssClass) {
923         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
924             if (this.invalidHandleClasses[i] == cssClass) {
925                 delete this.invalidHandleClasses[i];
926             }
927         }
928     },
929
930     /**
931      * Checks the tag exclusion list to see if this click should be ignored
932      * @method isValidHandleChild
933      * @param {HTMLElement} node the HTMLElement to evaluate
934      * @return {boolean} true if this is a valid tag type, false if not
935      */
936     isValidHandleChild: function(node) {
937
938         var valid = true;
939         // var n = (node.nodeName == "#text") ? node.parentNode : node;
940         var nodeName;
941         try {
942             nodeName = node.nodeName.toUpperCase();
943         } catch(e) {
944             nodeName = node.nodeName;
945         }
946         valid = valid && !this.invalidHandleTypes[nodeName];
947         valid = valid && !this.invalidHandleIds[node.id];
948
949         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
950             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
951         }
952
953
954         return valid;
955
956     },
957
958     /**
959      * Create the array of horizontal tick marks if an interval was specified
960      * in setXConstraint().
961      * @method setXTicks
962      * @private
963      */
964     setXTicks: function(iStartX, iTickSize) {
965         this.xTicks = [];
966         this.xTickSize = iTickSize;
967
968         var tickMap = {};
969
970         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
971             if (!tickMap[i]) {
972                 this.xTicks[this.xTicks.length] = i;
973                 tickMap[i] = true;
974             }
975         }
976
977         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
978             if (!tickMap[i]) {
979                 this.xTicks[this.xTicks.length] = i;
980                 tickMap[i] = true;
981             }
982         }
983
984         this.xTicks.sort(this.DDM.numericSort) ;
985     },
986
987     /**
988      * Create the array of vertical tick marks if an interval was specified in
989      * setYConstraint().
990      * @method setYTicks
991      * @private
992      */
993     setYTicks: function(iStartY, iTickSize) {
994         this.yTicks = [];
995         this.yTickSize = iTickSize;
996
997         var tickMap = {};
998
999         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
1000             if (!tickMap[i]) {
1001                 this.yTicks[this.yTicks.length] = i;
1002                 tickMap[i] = true;
1003             }
1004         }
1005
1006         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
1007             if (!tickMap[i]) {
1008                 this.yTicks[this.yTicks.length] = i;
1009                 tickMap[i] = true;
1010             }
1011         }
1012
1013         this.yTicks.sort(this.DDM.numericSort) ;
1014     },
1015
1016     /**
1017      * By default, the element can be dragged any place on the screen.  Use
1018      * this method to limit the horizontal travel of the element.  Pass in
1019      * 0,0 for the parameters if you want to lock the drag to the y axis.
1020      * @method setXConstraint
1021      * @param {int} iLeft the number of pixels the element can move to the left
1022      * @param {int} iRight the number of pixels the element can move to the
1023      * right
1024      * @param {int} iTickSize optional parameter for specifying that the
1025      * element
1026      * should move iTickSize pixels at a time.
1027      */
1028     setXConstraint: function(iLeft, iRight, iTickSize) {
1029         this.leftConstraint = iLeft;
1030         this.rightConstraint = iRight;
1031
1032         this.minX = this.initPageX - iLeft;
1033         this.maxX = this.initPageX + iRight;
1034         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
1035
1036         this.constrainX = true;
1037     },
1038
1039     /**
1040      * Clears any constraints applied to this instance.  Also clears ticks
1041      * since they can't exist independent of a constraint at this time.
1042      * @method clearConstraints
1043      */
1044     clearConstraints: function() {
1045         this.constrainX = false;
1046         this.constrainY = false;
1047         this.clearTicks();
1048     },
1049
1050     /**
1051      * Clears any tick interval defined for this instance
1052      * @method clearTicks
1053      */
1054     clearTicks: function() {
1055         this.xTicks = null;
1056         this.yTicks = null;
1057         this.xTickSize = 0;
1058         this.yTickSize = 0;
1059     },
1060
1061     /**
1062      * By default, the element can be dragged any place on the screen.  Set
1063      * this to limit the vertical travel of the element.  Pass in 0,0 for the
1064      * parameters if you want to lock the drag to the x axis.
1065      * @method setYConstraint
1066      * @param {int} iUp the number of pixels the element can move up
1067      * @param {int} iDown the number of pixels the element can move down
1068      * @param {int} iTickSize optional parameter for specifying that the
1069      * element should move iTickSize pixels at a time.
1070      */
1071     setYConstraint: function(iUp, iDown, iTickSize) {
1072         this.topConstraint = iUp;
1073         this.bottomConstraint = iDown;
1074
1075         this.minY = this.initPageY - iUp;
1076         this.maxY = this.initPageY + iDown;
1077         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
1078
1079         this.constrainY = true;
1080
1081     },
1082
1083     /**
1084      * resetConstraints must be called if you manually reposition a dd element.
1085      * @method resetConstraints
1086      * @param {boolean} maintainOffset
1087      */
1088     resetConstraints: function() {
1089
1090
1091         // Maintain offsets if necessary
1092         if (this.initPageX || this.initPageX === 0) {
1093             // figure out how much this thing has moved
1094             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
1095             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
1096
1097             this.setInitPosition(dx, dy);
1098
1099         // This is the first time we have detected the element's position
1100         } else {
1101             this.setInitPosition();
1102         }
1103
1104         if (this.constrainX) {
1105             this.setXConstraint( this.leftConstraint,
1106                                  this.rightConstraint,
1107                                  this.xTickSize        );
1108         }
1109
1110         if (this.constrainY) {
1111             this.setYConstraint( this.topConstraint,
1112                                  this.bottomConstraint,
1113                                  this.yTickSize         );
1114         }
1115     },
1116
1117     /**
1118      * Normally the drag element is moved pixel by pixel, but we can specify
1119      * that it move a number of pixels at a time.  This method resolves the
1120      * location when we have it set up like this.
1121      * @method getTick
1122      * @param {int} val where we want to place the object
1123      * @param {int[]} tickArray sorted array of valid points
1124      * @return {int} the closest tick
1125      * @private
1126      */
1127     getTick: function(val, tickArray) {
1128
1129         if (!tickArray) {
1130             // If tick interval is not defined, it is effectively 1 pixel,
1131             // so we return the value passed to us.
1132             return val;
1133         } else if (tickArray[0] >= val) {
1134             // The value is lower than the first tick, so we return the first
1135             // tick.
1136             return tickArray[0];
1137         } else {
1138             for (var i=0, len=tickArray.length; i<len; ++i) {
1139                 var next = i + 1;
1140                 if (tickArray[next] && tickArray[next] >= val) {
1141                     var diff1 = val - tickArray[i];
1142                     var diff2 = tickArray[next] - val;
1143                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
1144                 }
1145             }
1146
1147             // The value is larger than the last tick, so we return the last
1148             // tick.
1149             return tickArray[tickArray.length - 1];
1150         }
1151     },
1152
1153     /**
1154      * toString method
1155      * @method toString
1156      * @return {string} string representation of the dd obj
1157      */
1158     toString: function() {
1159         return ("DragDrop " + this.id);
1160     }
1161
1162 });
1163
1164 })();
1165 /*
1166  * Based on:
1167  * Ext JS Library 1.1.1
1168  * Copyright(c) 2006-2007, Ext JS, LLC.
1169  *
1170  * Originally Released Under LGPL - original licence link has changed is not relivant.
1171  *
1172  * Fork - LGPL
1173  * <script type="text/javascript">
1174  */
1175
1176
1177 /**
1178  * The drag and drop utility provides a framework for building drag and drop
1179  * applications.  In addition to enabling drag and drop for specific elements,
1180  * the drag and drop elements are tracked by the manager class, and the
1181  * interactions between the various elements are tracked during the drag and
1182  * the implementing code is notified about these important moments.
1183  */
1184
1185 // Only load the library once.  Rewriting the manager class would orphan
1186 // existing drag and drop instances.
1187 if (!Roo.dd.DragDropMgr) {
1188
1189 /**
1190  * @class Roo.dd.DragDropMgr
1191  * DragDropMgr is a singleton that tracks the element interaction for
1192  * all DragDrop items in the window.  Generally, you will not call
1193  * this class directly, but it does have helper methods that could
1194  * be useful in your DragDrop implementations.
1195  * @singleton
1196  */
1197 Roo.dd.DragDropMgr = function() {
1198
1199     var Event = Roo.EventManager;
1200
1201     return {
1202
1203         /**
1204          * Two dimensional Array of registered DragDrop objects.  The first
1205          * dimension is the DragDrop item group, the second the DragDrop
1206          * object.
1207          * @property ids
1208          * @type {string: string}
1209          * @private
1210          * @static
1211          */
1212         ids: {},
1213
1214         /**
1215          * Array of element ids defined as drag handles.  Used to determine
1216          * if the element that generated the mousedown event is actually the
1217          * handle and not the html element itself.
1218          * @property handleIds
1219          * @type {string: string}
1220          * @private
1221          * @static
1222          */
1223         handleIds: {},
1224
1225         /**
1226          * the DragDrop object that is currently being dragged
1227          * @property dragCurrent
1228          * @type DragDrop
1229          * @private
1230          * @static
1231          **/
1232         dragCurrent: null,
1233
1234         /**
1235          * the DragDrop object(s) that are being hovered over
1236          * @property dragOvers
1237          * @type Array
1238          * @private
1239          * @static
1240          */
1241         dragOvers: {},
1242
1243         /**
1244          * the X distance between the cursor and the object being dragged
1245          * @property deltaX
1246          * @type int
1247          * @private
1248          * @static
1249          */
1250         deltaX: 0,
1251
1252         /**
1253          * the Y distance between the cursor and the object being dragged
1254          * @property deltaY
1255          * @type int
1256          * @private
1257          * @static
1258          */
1259         deltaY: 0,
1260
1261         /**
1262          * Flag to determine if we should prevent the default behavior of the
1263          * events we define. By default this is true, but this can be set to
1264          * false if you need the default behavior (not recommended)
1265          * @property preventDefault
1266          * @type boolean
1267          * @static
1268          */
1269         preventDefault: true,
1270
1271         /**
1272          * Flag to determine if we should stop the propagation of the events
1273          * we generate. This is true by default but you may want to set it to
1274          * false if the html element contains other features that require the
1275          * mouse click.
1276          * @property stopPropagation
1277          * @type boolean
1278          * @static
1279          */
1280         stopPropagation: true,
1281
1282         /**
1283          * Internal flag that is set to true when drag and drop has been
1284          * intialized
1285          * @property initialized
1286          * @private
1287          * @static
1288          */
1289         initalized: false,
1290
1291         /**
1292          * All drag and drop can be disabled.
1293          * @property locked
1294          * @private
1295          * @static
1296          */
1297         locked: false,
1298
1299         /**
1300          * Called the first time an element is registered.
1301          * @method init
1302          * @private
1303          * @static
1304          */
1305         init: function() {
1306             this.initialized = true;
1307         },
1308
1309         /**
1310          * In point mode, drag and drop interaction is defined by the
1311          * location of the cursor during the drag/drop
1312          * @property POINT
1313          * @type int
1314          * @static
1315          */
1316         POINT: 0,
1317
1318         /**
1319          * In intersect mode, drag and drop interactio nis defined by the
1320          * overlap of two or more drag and drop objects.
1321          * @property INTERSECT
1322          * @type int
1323          * @static
1324          */
1325         INTERSECT: 1,
1326
1327         /**
1328          * The current drag and drop mode.  Default: POINT
1329          * @property mode
1330          * @type int
1331          * @static
1332          */
1333         mode: 0,
1334
1335         /**
1336          * Runs method on all drag and drop objects
1337          * @method _execOnAll
1338          * @private
1339          * @static
1340          */
1341         _execOnAll: function(sMethod, args) {
1342             for (var i in this.ids) {
1343                 for (var j in this.ids[i]) {
1344                     var oDD = this.ids[i][j];
1345                     if (! this.isTypeOfDD(oDD)) {
1346                         continue;
1347                     }
1348                     oDD[sMethod].apply(oDD, args);
1349                 }
1350             }
1351         },
1352
1353         /**
1354          * Drag and drop initialization.  Sets up the global event handlers
1355          * @method _onLoad
1356          * @private
1357          * @static
1358          */
1359         _onLoad: function() {
1360
1361             this.init();
1362
1363
1364             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
1365             Event.on(document, "mousemove", this.handleMouseMove, this, true);
1366             Event.on(window,   "unload",    this._onUnload, this, true);
1367             Event.on(window,   "resize",    this._onResize, this, true);
1368             // Event.on(window,   "mouseout",    this._test);
1369
1370         },
1371
1372         /**
1373          * Reset constraints on all drag and drop objs
1374          * @method _onResize
1375          * @private
1376          * @static
1377          */
1378         _onResize: function(e) {
1379             this._execOnAll("resetConstraints", []);
1380         },
1381
1382         /**
1383          * Lock all drag and drop functionality
1384          * @method lock
1385          * @static
1386          */
1387         lock: function() { this.locked = true; },
1388
1389         /**
1390          * Unlock all drag and drop functionality
1391          * @method unlock
1392          * @static
1393          */
1394         unlock: function() { this.locked = false; },
1395
1396         /**
1397          * Is drag and drop locked?
1398          * @method isLocked
1399          * @return {boolean} True if drag and drop is locked, false otherwise.
1400          * @static
1401          */
1402         isLocked: function() { return this.locked; },
1403
1404         /**
1405          * Location cache that is set for all drag drop objects when a drag is
1406          * initiated, cleared when the drag is finished.
1407          * @property locationCache
1408          * @private
1409          * @static
1410          */
1411         locationCache: {},
1412
1413         /**
1414          * Set useCache to false if you want to force object the lookup of each
1415          * drag and drop linked element constantly during a drag.
1416          * @property useCache
1417          * @type boolean
1418          * @static
1419          */
1420         useCache: true,
1421
1422         /**
1423          * The number of pixels that the mouse needs to move after the
1424          * mousedown before the drag is initiated.  Default=3;
1425          * @property clickPixelThresh
1426          * @type int
1427          * @static
1428          */
1429         clickPixelThresh: 3,
1430
1431         /**
1432          * The number of milliseconds after the mousedown event to initiate the
1433          * drag if we don't get a mouseup event. Default=1000
1434          * @property clickTimeThresh
1435          * @type int
1436          * @static
1437          */
1438         clickTimeThresh: 350,
1439
1440         /**
1441          * Flag that indicates that either the drag pixel threshold or the
1442          * mousdown time threshold has been met
1443          * @property dragThreshMet
1444          * @type boolean
1445          * @private
1446          * @static
1447          */
1448         dragThreshMet: false,
1449
1450         /**
1451          * Timeout used for the click time threshold
1452          * @property clickTimeout
1453          * @type Object
1454          * @private
1455          * @static
1456          */
1457         clickTimeout: null,
1458
1459         /**
1460          * The X position of the mousedown event stored for later use when a
1461          * drag threshold is met.
1462          * @property startX
1463          * @type int
1464          * @private
1465          * @static
1466          */
1467         startX: 0,
1468
1469         /**
1470          * The Y position of the mousedown event stored for later use when a
1471          * drag threshold is met.
1472          * @property startY
1473          * @type int
1474          * @private
1475          * @static
1476          */
1477         startY: 0,
1478
1479         /**
1480          * Each DragDrop instance must be registered with the DragDropMgr.
1481          * This is executed in DragDrop.init()
1482          * @method regDragDrop
1483          * @param {DragDrop} oDD the DragDrop object to register
1484          * @param {String} sGroup the name of the group this element belongs to
1485          * @static
1486          */
1487         regDragDrop: function(oDD, sGroup) {
1488             if (!this.initialized) { this.init(); }
1489
1490             if (!this.ids[sGroup]) {
1491                 this.ids[sGroup] = {};
1492             }
1493             this.ids[sGroup][oDD.id] = oDD;
1494         },
1495
1496         /**
1497          * Removes the supplied dd instance from the supplied group. Executed
1498          * by DragDrop.removeFromGroup, so don't call this function directly.
1499          * @method removeDDFromGroup
1500          * @private
1501          * @static
1502          */
1503         removeDDFromGroup: function(oDD, sGroup) {
1504             if (!this.ids[sGroup]) {
1505                 this.ids[sGroup] = {};
1506             }
1507
1508             var obj = this.ids[sGroup];
1509             if (obj && obj[oDD.id]) {
1510                 delete obj[oDD.id];
1511             }
1512         },
1513
1514         /**
1515          * Unregisters a drag and drop item.  This is executed in
1516          * DragDrop.unreg, use that method instead of calling this directly.
1517          * @method _remove
1518          * @private
1519          * @static
1520          */
1521         _remove: function(oDD) {
1522             for (var g in oDD.groups) {
1523                 if (g && this.ids[g][oDD.id]) {
1524                     delete this.ids[g][oDD.id];
1525                 }
1526             }
1527             delete this.handleIds[oDD.id];
1528         },
1529
1530         /**
1531          * Each DragDrop handle element must be registered.  This is done
1532          * automatically when executing DragDrop.setHandleElId()
1533          * @method regHandle
1534          * @param {String} sDDId the DragDrop id this element is a handle for
1535          * @param {String} sHandleId the id of the element that is the drag
1536          * handle
1537          * @static
1538          */
1539         regHandle: function(sDDId, sHandleId) {
1540             if (!this.handleIds[sDDId]) {
1541                 this.handleIds[sDDId] = {};
1542             }
1543             this.handleIds[sDDId][sHandleId] = sHandleId;
1544         },
1545
1546         /**
1547          * Utility function to determine if a given element has been
1548          * registered as a drag drop item.
1549          * @method isDragDrop
1550          * @param {String} id the element id to check
1551          * @return {boolean} true if this element is a DragDrop item,
1552          * false otherwise
1553          * @static
1554          */
1555         isDragDrop: function(id) {
1556             return ( this.getDDById(id) ) ? true : false;
1557         },
1558
1559         /**
1560          * Returns the drag and drop instances that are in all groups the
1561          * passed in instance belongs to.
1562          * @method getRelated
1563          * @param {DragDrop} p_oDD the obj to get related data for
1564          * @param {boolean} bTargetsOnly if true, only return targetable objs
1565          * @return {DragDrop[]} the related instances
1566          * @static
1567          */
1568         getRelated: function(p_oDD, bTargetsOnly) {
1569             var oDDs = [];
1570             for (var i in p_oDD.groups) {
1571                 for (j in this.ids[i]) {
1572                     var dd = this.ids[i][j];
1573                     if (! this.isTypeOfDD(dd)) {
1574                         continue;
1575                     }
1576                     if (!bTargetsOnly || dd.isTarget) {
1577                         oDDs[oDDs.length] = dd;
1578                     }
1579                 }
1580             }
1581
1582             return oDDs;
1583         },
1584
1585         /**
1586          * Returns true if the specified dd target is a legal target for
1587          * the specifice drag obj
1588          * @method isLegalTarget
1589          * @param {DragDrop} the drag obj
1590          * @param {DragDrop} the target
1591          * @return {boolean} true if the target is a legal target for the
1592          * dd obj
1593          * @static
1594          */
1595         isLegalTarget: function (oDD, oTargetDD) {
1596             var targets = this.getRelated(oDD, true);
1597             for (var i=0, len=targets.length;i<len;++i) {
1598                 if (targets[i].id == oTargetDD.id) {
1599                     return true;
1600                 }
1601             }
1602
1603             return false;
1604         },
1605
1606         /**
1607          * My goal is to be able to transparently determine if an object is
1608          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
1609          * returns "object", oDD.constructor.toString() always returns
1610          * "DragDrop" and not the name of the subclass.  So for now it just
1611          * evaluates a well-known variable in DragDrop.
1612          * @method isTypeOfDD
1613          * @param {Object} the object to evaluate
1614          * @return {boolean} true if typeof oDD = DragDrop
1615          * @static
1616          */
1617         isTypeOfDD: function (oDD) {
1618             return (oDD && oDD.__ygDragDrop);
1619         },
1620
1621         /**
1622          * Utility function to determine if a given element has been
1623          * registered as a drag drop handle for the given Drag Drop object.
1624          * @method isHandle
1625          * @param {String} id the element id to check
1626          * @return {boolean} true if this element is a DragDrop handle, false
1627          * otherwise
1628          * @static
1629          */
1630         isHandle: function(sDDId, sHandleId) {
1631             return ( this.handleIds[sDDId] &&
1632                             this.handleIds[sDDId][sHandleId] );
1633         },
1634
1635         /**
1636          * Returns the DragDrop instance for a given id
1637          * @method getDDById
1638          * @param {String} id the id of the DragDrop object
1639          * @return {DragDrop} the drag drop object, null if it is not found
1640          * @static
1641          */
1642         getDDById: function(id) {
1643             for (var i in this.ids) {
1644                 if (this.ids[i][id]) {
1645                     return this.ids[i][id];
1646                 }
1647             }
1648             return null;
1649         },
1650
1651         /**
1652          * Fired after a registered DragDrop object gets the mousedown event.
1653          * Sets up the events required to track the object being dragged
1654          * @method handleMouseDown
1655          * @param {Event} e the event
1656          * @param oDD the DragDrop object being dragged
1657          * @private
1658          * @static
1659          */
1660         handleMouseDown: function(e, oDD) {
1661             if(Roo.QuickTips){
1662                 Roo.QuickTips.disable();
1663             }
1664             this.currentTarget = e.getTarget();
1665
1666             this.dragCurrent = oDD;
1667
1668             var el = oDD.getEl();
1669
1670             // track start position
1671             this.startX = e.getPageX();
1672             this.startY = e.getPageY();
1673
1674             this.deltaX = this.startX - el.offsetLeft;
1675             this.deltaY = this.startY - el.offsetTop;
1676
1677             this.dragThreshMet = false;
1678
1679             this.clickTimeout = setTimeout(
1680                     function() {
1681                         var DDM = Roo.dd.DDM;
1682                         DDM.startDrag(DDM.startX, DDM.startY);
1683                     },
1684                     this.clickTimeThresh );
1685         },
1686
1687         /**
1688          * Fired when either the drag pixel threshol or the mousedown hold
1689          * time threshold has been met.
1690          * @method startDrag
1691          * @param x {int} the X position of the original mousedown
1692          * @param y {int} the Y position of the original mousedown
1693          * @static
1694          */
1695         startDrag: function(x, y) {
1696             clearTimeout(this.clickTimeout);
1697             if (this.dragCurrent) {
1698                 this.dragCurrent.b4StartDrag(x, y);
1699                 this.dragCurrent.startDrag(x, y);
1700             }
1701             this.dragThreshMet = true;
1702         },
1703
1704         /**
1705          * Internal function to handle the mouseup event.  Will be invoked
1706          * from the context of the document.
1707          * @method handleMouseUp
1708          * @param {Event} e the event
1709          * @private
1710          * @static
1711          */
1712         handleMouseUp: function(e) {
1713
1714             if(Roo.QuickTips){
1715                 Roo.QuickTips.enable();
1716             }
1717             if (! this.dragCurrent) {
1718                 return;
1719             }
1720
1721             clearTimeout(this.clickTimeout);
1722
1723             if (this.dragThreshMet) {
1724                 this.fireEvents(e, true);
1725             } else {
1726             }
1727
1728             this.stopDrag(e);
1729
1730             this.stopEvent(e);
1731         },
1732
1733         /**
1734          * Utility to stop event propagation and event default, if these
1735          * features are turned on.
1736          * @method stopEvent
1737          * @param {Event} e the event as returned by this.getEvent()
1738          * @static
1739          */
1740         stopEvent: function(e){
1741             if(this.stopPropagation) {
1742                 e.stopPropagation();
1743             }
1744
1745             if (this.preventDefault) {
1746                 e.preventDefault();
1747             }
1748         },
1749
1750         /**
1751          * Internal function to clean up event handlers after the drag
1752          * operation is complete
1753          * @method stopDrag
1754          * @param {Event} e the event
1755          * @private
1756          * @static
1757          */
1758         stopDrag: function(e) {
1759             // Fire the drag end event for the item that was dragged
1760             if (this.dragCurrent) {
1761                 if (this.dragThreshMet) {
1762                     this.dragCurrent.b4EndDrag(e);
1763                     this.dragCurrent.endDrag(e);
1764                 }
1765
1766                 this.dragCurrent.onMouseUp(e);
1767             }
1768
1769             this.dragCurrent = null;
1770             this.dragOvers = {};
1771         },
1772
1773         /**
1774          * Internal function to handle the mousemove event.  Will be invoked
1775          * from the context of the html element.
1776          *
1777          * @TODO figure out what we can do about mouse events lost when the
1778          * user drags objects beyond the window boundary.  Currently we can
1779          * detect this in internet explorer by verifying that the mouse is
1780          * down during the mousemove event.  Firefox doesn't give us the
1781          * button state on the mousemove event.
1782          * @method handleMouseMove
1783          * @param {Event} e the event
1784          * @private
1785          * @static
1786          */
1787         handleMouseMove: function(e) {
1788             if (! this.dragCurrent) {
1789                 return true;
1790             }
1791
1792             // var button = e.which || e.button;
1793
1794             // check for IE mouseup outside of page boundary
1795             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
1796                 this.stopEvent(e);
1797                 return this.handleMouseUp(e);
1798             }
1799
1800             if (!this.dragThreshMet) {
1801                 var diffX = Math.abs(this.startX - e.getPageX());
1802                 var diffY = Math.abs(this.startY - e.getPageY());
1803                 if (diffX > this.clickPixelThresh ||
1804                             diffY > this.clickPixelThresh) {
1805                     this.startDrag(this.startX, this.startY);
1806                 }
1807             }
1808
1809             if (this.dragThreshMet) {
1810                 this.dragCurrent.b4Drag(e);
1811                 this.dragCurrent.onDrag(e);
1812                 if(!this.dragCurrent.moveOnly){
1813                     this.fireEvents(e, false);
1814                 }
1815             }
1816
1817             this.stopEvent(e);
1818
1819             return true;
1820         },
1821
1822         /**
1823          * Iterates over all of the DragDrop elements to find ones we are
1824          * hovering over or dropping on
1825          * @method fireEvents
1826          * @param {Event} e the event
1827          * @param {boolean} isDrop is this a drop op or a mouseover op?
1828          * @private
1829          * @static
1830          */
1831         fireEvents: function(e, isDrop) {
1832             var dc = this.dragCurrent;
1833
1834             // If the user did the mouse up outside of the window, we could
1835             // get here even though we have ended the drag.
1836             if (!dc || dc.isLocked()) {
1837                 return;
1838             }
1839
1840             var pt = e.getPoint();
1841
1842             // cache the previous dragOver array
1843             var oldOvers = [];
1844
1845             var outEvts   = [];
1846             var overEvts  = [];
1847             var dropEvts  = [];
1848             var enterEvts = [];
1849
1850             // Check to see if the object(s) we were hovering over is no longer
1851             // being hovered over so we can fire the onDragOut event
1852             for (var i in this.dragOvers) {
1853
1854                 var ddo = this.dragOvers[i];
1855
1856                 if (! this.isTypeOfDD(ddo)) {
1857                     continue;
1858                 }
1859
1860                 if (! this.isOverTarget(pt, ddo, this.mode)) {
1861                     outEvts.push( ddo );
1862                 }
1863
1864                 oldOvers[i] = true;
1865                 delete this.dragOvers[i];
1866             }
1867
1868             for (var sGroup in dc.groups) {
1869
1870                 if ("string" != typeof sGroup) {
1871                     continue;
1872                 }
1873
1874                 for (i in this.ids[sGroup]) {
1875                     var oDD = this.ids[sGroup][i];
1876                     if (! this.isTypeOfDD(oDD)) {
1877                         continue;
1878                     }
1879
1880                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
1881                         if (this.isOverTarget(pt, oDD, this.mode)) {
1882                             // look for drop interactions
1883                             if (isDrop) {
1884                                 dropEvts.push( oDD );
1885                             // look for drag enter and drag over interactions
1886                             } else {
1887
1888                                 // initial drag over: dragEnter fires
1889                                 if (!oldOvers[oDD.id]) {
1890                                     enterEvts.push( oDD );
1891                                 // subsequent drag overs: dragOver fires
1892                                 } else {
1893                                     overEvts.push( oDD );
1894                                 }
1895
1896                                 this.dragOvers[oDD.id] = oDD;
1897                             }
1898                         }
1899                     }
1900                 }
1901             }
1902
1903             if (this.mode) {
1904                 if (outEvts.length) {
1905                     dc.b4DragOut(e, outEvts);
1906                     dc.onDragOut(e, outEvts);
1907                 }
1908
1909                 if (enterEvts.length) {
1910                     dc.onDragEnter(e, enterEvts);
1911                 }
1912
1913                 if (overEvts.length) {
1914                     dc.b4DragOver(e, overEvts);
1915                     dc.onDragOver(e, overEvts);
1916                 }
1917
1918                 if (dropEvts.length) {
1919                     dc.b4DragDrop(e, dropEvts);
1920                     dc.onDragDrop(e, dropEvts);
1921                 }
1922
1923             } else {
1924                 // fire dragout events
1925                 var len = 0;
1926                 for (i=0, len=outEvts.length; i<len; ++i) {
1927                     dc.b4DragOut(e, outEvts[i].id);
1928                     dc.onDragOut(e, outEvts[i].id);
1929                 }
1930
1931                 // fire enter events
1932                 for (i=0,len=enterEvts.length; i<len; ++i) {
1933                     // dc.b4DragEnter(e, oDD.id);
1934                     dc.onDragEnter(e, enterEvts[i].id);
1935                 }
1936
1937                 // fire over events
1938                 for (i=0,len=overEvts.length; i<len; ++i) {
1939                     dc.b4DragOver(e, overEvts[i].id);
1940                     dc.onDragOver(e, overEvts[i].id);
1941                 }
1942
1943                 // fire drop events
1944                 for (i=0, len=dropEvts.length; i<len; ++i) {
1945                     dc.b4DragDrop(e, dropEvts[i].id);
1946                     dc.onDragDrop(e, dropEvts[i].id);
1947                 }
1948
1949             }
1950
1951             // notify about a drop that did not find a target
1952             if (isDrop && !dropEvts.length) {
1953                 dc.onInvalidDrop(e);
1954             }
1955
1956         },
1957
1958         /**
1959          * Helper function for getting the best match from the list of drag
1960          * and drop objects returned by the drag and drop events when we are
1961          * in INTERSECT mode.  It returns either the first object that the
1962          * cursor is over, or the object that has the greatest overlap with
1963          * the dragged element.
1964          * @method getBestMatch
1965          * @param  {DragDrop[]} dds The array of drag and drop objects
1966          * targeted
1967          * @return {DragDrop}       The best single match
1968          * @static
1969          */
1970         getBestMatch: function(dds) {
1971             var winner = null;
1972             // Return null if the input is not what we expect
1973             //if (!dds || !dds.length || dds.length == 0) {
1974                // winner = null;
1975             // If there is only one item, it wins
1976             //} else if (dds.length == 1) {
1977
1978             var len = dds.length;
1979
1980             if (len == 1) {
1981                 winner = dds[0];
1982             } else {
1983                 // Loop through the targeted items
1984                 for (var i=0; i<len; ++i) {
1985                     var dd = dds[i];
1986                     // If the cursor is over the object, it wins.  If the
1987                     // cursor is over multiple matches, the first one we come
1988                     // to wins.
1989                     if (dd.cursorIsOver) {
1990                         winner = dd;
1991                         break;
1992                     // Otherwise the object with the most overlap wins
1993                     } else {
1994                         if (!winner ||
1995                             winner.overlap.getArea() < dd.overlap.getArea()) {
1996                             winner = dd;
1997                         }
1998                     }
1999                 }
2000             }
2001
2002             return winner;
2003         },
2004
2005         /**
2006          * Refreshes the cache of the top-left and bottom-right points of the
2007          * drag and drop objects in the specified group(s).  This is in the
2008          * format that is stored in the drag and drop instance, so typical
2009          * usage is:
2010          * <code>
2011          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
2012          * </code>
2013          * Alternatively:
2014          * <code>
2015          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
2016          * </code>
2017          * @TODO this really should be an indexed array.  Alternatively this
2018          * method could accept both.
2019          * @method refreshCache
2020          * @param {Object} groups an associative array of groups to refresh
2021          * @static
2022          */
2023         refreshCache: function(groups) {
2024             for (var sGroup in groups) {
2025                 if ("string" != typeof sGroup) {
2026                     continue;
2027                 }
2028                 for (var i in this.ids[sGroup]) {
2029                     var oDD = this.ids[sGroup][i];
2030
2031                     if (this.isTypeOfDD(oDD)) {
2032                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
2033                         var loc = this.getLocation(oDD);
2034                         if (loc) {
2035                             this.locationCache[oDD.id] = loc;
2036                         } else {
2037                             delete this.locationCache[oDD.id];
2038                             // this will unregister the drag and drop object if
2039                             // the element is not in a usable state
2040                             // oDD.unreg();
2041                         }
2042                     }
2043                 }
2044             }
2045         },
2046
2047         /**
2048          * This checks to make sure an element exists and is in the DOM.  The
2049          * main purpose is to handle cases where innerHTML is used to remove
2050          * drag and drop objects from the DOM.  IE provides an 'unspecified
2051          * error' when trying to access the offsetParent of such an element
2052          * @method verifyEl
2053          * @param {HTMLElement} el the element to check
2054          * @return {boolean} true if the element looks usable
2055          * @static
2056          */
2057         verifyEl: function(el) {
2058             if (el) {
2059                 var parent;
2060                 if(Roo.isIE){
2061                     try{
2062                         parent = el.offsetParent;
2063                     }catch(e){}
2064                 }else{
2065                     parent = el.offsetParent;
2066                 }
2067                 if (parent) {
2068                     return true;
2069                 }
2070             }
2071
2072             return false;
2073         },
2074
2075         /**
2076          * Returns a Region object containing the drag and drop element's position
2077          * and size, including the padding configured for it
2078          * @method getLocation
2079          * @param {DragDrop} oDD the drag and drop object to get the
2080          *                       location for
2081          * @return {Roo.lib.Region} a Region object representing the total area
2082          *                             the element occupies, including any padding
2083          *                             the instance is configured for.
2084          * @static
2085          */
2086         getLocation: function(oDD) {
2087             if (! this.isTypeOfDD(oDD)) {
2088                 return null;
2089             }
2090
2091             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
2092
2093             try {
2094                 pos= Roo.lib.Dom.getXY(el);
2095             } catch (e) { }
2096
2097             if (!pos) {
2098                 return null;
2099             }
2100
2101             x1 = pos[0];
2102             x2 = x1 + el.offsetWidth;
2103             y1 = pos[1];
2104             y2 = y1 + el.offsetHeight;
2105
2106             t = y1 - oDD.padding[0];
2107             r = x2 + oDD.padding[1];
2108             b = y2 + oDD.padding[2];
2109             l = x1 - oDD.padding[3];
2110
2111             return new Roo.lib.Region( t, r, b, l );
2112         },
2113
2114         /**
2115          * Checks the cursor location to see if it over the target
2116          * @method isOverTarget
2117          * @param {Roo.lib.Point} pt The point to evaluate
2118          * @param {DragDrop} oTarget the DragDrop object we are inspecting
2119          * @return {boolean} true if the mouse is over the target
2120          * @private
2121          * @static
2122          */
2123         isOverTarget: function(pt, oTarget, intersect) {
2124             // use cache if available
2125             var loc = this.locationCache[oTarget.id];
2126             if (!loc || !this.useCache) {
2127                 loc = this.getLocation(oTarget);
2128                 this.locationCache[oTarget.id] = loc;
2129
2130             }
2131
2132             if (!loc) {
2133                 return false;
2134             }
2135
2136             oTarget.cursorIsOver = loc.contains( pt );
2137
2138             // DragDrop is using this as a sanity check for the initial mousedown
2139             // in this case we are done.  In POINT mode, if the drag obj has no
2140             // contraints, we are also done. Otherwise we need to evaluate the
2141             // location of the target as related to the actual location of the
2142             // dragged element.
2143             var dc = this.dragCurrent;
2144             if (!dc || !dc.getTargetCoord ||
2145                     (!intersect && !dc.constrainX && !dc.constrainY)) {
2146                 return oTarget.cursorIsOver;
2147             }
2148
2149             oTarget.overlap = null;
2150
2151             // Get the current location of the drag element, this is the
2152             // location of the mouse event less the delta that represents
2153             // where the original mousedown happened on the element.  We
2154             // need to consider constraints and ticks as well.
2155             var pos = dc.getTargetCoord(pt.x, pt.y);
2156
2157             var el = dc.getDragEl();
2158             var curRegion = new Roo.lib.Region( pos.y,
2159                                                    pos.x + el.offsetWidth,
2160                                                    pos.y + el.offsetHeight,
2161                                                    pos.x );
2162
2163             var overlap = curRegion.intersect(loc);
2164
2165             if (overlap) {
2166                 oTarget.overlap = overlap;
2167                 return (intersect) ? true : oTarget.cursorIsOver;
2168             } else {
2169                 return false;
2170             }
2171         },
2172
2173         /**
2174          * unload event handler
2175          * @method _onUnload
2176          * @private
2177          * @static
2178          */
2179         _onUnload: function(e, me) {
2180             Roo.dd.DragDropMgr.unregAll();
2181         },
2182
2183         /**
2184          * Cleans up the drag and drop events and objects.
2185          * @method unregAll
2186          * @private
2187          * @static
2188          */
2189         unregAll: function() {
2190
2191             if (this.dragCurrent) {
2192                 this.stopDrag();
2193                 this.dragCurrent = null;
2194             }
2195
2196             this._execOnAll("unreg", []);
2197
2198             for (i in this.elementCache) {
2199                 delete this.elementCache[i];
2200             }
2201
2202             this.elementCache = {};
2203             this.ids = {};
2204         },
2205
2206         /**
2207          * A cache of DOM elements
2208          * @property elementCache
2209          * @private
2210          * @static
2211          */
2212         elementCache: {},
2213
2214         /**
2215          * Get the wrapper for the DOM element specified
2216          * @method getElWrapper
2217          * @param {String} id the id of the element to get
2218          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
2219          * @private
2220          * @deprecated This wrapper isn't that useful
2221          * @static
2222          */
2223         getElWrapper: function(id) {
2224             var oWrapper = this.elementCache[id];
2225             if (!oWrapper || !oWrapper.el) {
2226                 oWrapper = this.elementCache[id] =
2227                     new this.ElementWrapper(Roo.getDom(id));
2228             }
2229             return oWrapper;
2230         },
2231
2232         /**
2233          * Returns the actual DOM element
2234          * @method getElement
2235          * @param {String} id the id of the elment to get
2236          * @return {Object} The element
2237          * @deprecated use Roo.getDom instead
2238          * @static
2239          */
2240         getElement: function(id) {
2241             return Roo.getDom(id);
2242         },
2243
2244         /**
2245          * Returns the style property for the DOM element (i.e.,
2246          * document.getElById(id).style)
2247          * @method getCss
2248          * @param {String} id the id of the elment to get
2249          * @return {Object} The style property of the element
2250          * @deprecated use Roo.getDom instead
2251          * @static
2252          */
2253         getCss: function(id) {
2254             var el = Roo.getDom(id);
2255             return (el) ? el.style : null;
2256         },
2257
2258         /**
2259          * Inner class for cached elements
2260          * @class DragDropMgr.ElementWrapper
2261          * @for DragDropMgr
2262          * @private
2263          * @deprecated
2264          */
2265         ElementWrapper: function(el) {
2266                 /**
2267                  * The element
2268                  * @property el
2269                  */
2270                 this.el = el || null;
2271                 /**
2272                  * The element id
2273                  * @property id
2274                  */
2275                 this.id = this.el && el.id;
2276                 /**
2277                  * A reference to the style property
2278                  * @property css
2279                  */
2280                 this.css = this.el && el.style;
2281             },
2282
2283         /**
2284          * Returns the X position of an html element
2285          * @method getPosX
2286          * @param el the element for which to get the position
2287          * @return {int} the X coordinate
2288          * @for DragDropMgr
2289          * @deprecated use Roo.lib.Dom.getX instead
2290          * @static
2291          */
2292         getPosX: function(el) {
2293             return Roo.lib.Dom.getX(el);
2294         },
2295
2296         /**
2297          * Returns the Y position of an html element
2298          * @method getPosY
2299          * @param el the element for which to get the position
2300          * @return {int} the Y coordinate
2301          * @deprecated use Roo.lib.Dom.getY instead
2302          * @static
2303          */
2304         getPosY: function(el) {
2305             return Roo.lib.Dom.getY(el);
2306         },
2307
2308         /**
2309          * Swap two nodes.  In IE, we use the native method, for others we
2310          * emulate the IE behavior
2311          * @method swapNode
2312          * @param n1 the first node to swap
2313          * @param n2 the other node to swap
2314          * @static
2315          */
2316         swapNode: function(n1, n2) {
2317             if (n1.swapNode) {
2318                 n1.swapNode(n2);
2319             } else {
2320                 var p = n2.parentNode;
2321                 var s = n2.nextSibling;
2322
2323                 if (s == n1) {
2324                     p.insertBefore(n1, n2);
2325                 } else if (n2 == n1.nextSibling) {
2326                     p.insertBefore(n2, n1);
2327                 } else {
2328                     n1.parentNode.replaceChild(n2, n1);
2329                     p.insertBefore(n1, s);
2330                 }
2331             }
2332         },
2333
2334         /**
2335          * Returns the current scroll position
2336          * @method getScroll
2337          * @private
2338          * @static
2339          */
2340         getScroll: function () {
2341             var t, l, dde=document.documentElement, db=document.body;
2342             if (dde && (dde.scrollTop || dde.scrollLeft)) {
2343                 t = dde.scrollTop;
2344                 l = dde.scrollLeft;
2345             } else if (db) {
2346                 t = db.scrollTop;
2347                 l = db.scrollLeft;
2348             } else {
2349
2350             }
2351             return { top: t, left: l };
2352         },
2353
2354         /**
2355          * Returns the specified element style property
2356          * @method getStyle
2357          * @param {HTMLElement} el          the element
2358          * @param {string}      styleProp   the style property
2359          * @return {string} The value of the style property
2360          * @deprecated use Roo.lib.Dom.getStyle
2361          * @static
2362          */
2363         getStyle: function(el, styleProp) {
2364             return Roo.fly(el).getStyle(styleProp);
2365         },
2366
2367         /**
2368          * Gets the scrollTop
2369          * @method getScrollTop
2370          * @return {int} the document's scrollTop
2371          * @static
2372          */
2373         getScrollTop: function () { return this.getScroll().top; },
2374
2375         /**
2376          * Gets the scrollLeft
2377          * @method getScrollLeft
2378          * @return {int} the document's scrollTop
2379          * @static
2380          */
2381         getScrollLeft: function () { return this.getScroll().left; },
2382
2383         /**
2384          * Sets the x/y position of an element to the location of the
2385          * target element.
2386          * @method moveToEl
2387          * @param {HTMLElement} moveEl      The element to move
2388          * @param {HTMLElement} targetEl    The position reference element
2389          * @static
2390          */
2391         moveToEl: function (moveEl, targetEl) {
2392             var aCoord = Roo.lib.Dom.getXY(targetEl);
2393             Roo.lib.Dom.setXY(moveEl, aCoord);
2394         },
2395
2396         /**
2397          * Numeric array sort function
2398          * @method numericSort
2399          * @static
2400          */
2401         numericSort: function(a, b) { return (a - b); },
2402
2403         /**
2404          * Internal counter
2405          * @property _timeoutCount
2406          * @private
2407          * @static
2408          */
2409         _timeoutCount: 0,
2410
2411         /**
2412          * Trying to make the load order less important.  Without this we get
2413          * an error if this file is loaded before the Event Utility.
2414          * @method _addListeners
2415          * @private
2416          * @static
2417          */
2418         _addListeners: function() {
2419             var DDM = Roo.dd.DDM;
2420             if ( Roo.lib.Event && document ) {
2421                 DDM._onLoad();
2422             } else {
2423                 if (DDM._timeoutCount > 2000) {
2424                 } else {
2425                     setTimeout(DDM._addListeners, 10);
2426                     if (document && document.body) {
2427                         DDM._timeoutCount += 1;
2428                     }
2429                 }
2430             }
2431         },
2432
2433         /**
2434          * Recursively searches the immediate parent and all child nodes for
2435          * the handle element in order to determine wheter or not it was
2436          * clicked.
2437          * @method handleWasClicked
2438          * @param node the html element to inspect
2439          * @static
2440          */
2441         handleWasClicked: function(node, id) {
2442             if (this.isHandle(id, node.id)) {
2443                 return true;
2444             } else {
2445                 // check to see if this is a text node child of the one we want
2446                 var p = node.parentNode;
2447
2448                 while (p) {
2449                     if (this.isHandle(id, p.id)) {
2450                         return true;
2451                     } else {
2452                         p = p.parentNode;
2453                     }
2454                 }
2455             }
2456
2457             return false;
2458         }
2459
2460     };
2461
2462 }();
2463
2464 // shorter alias, save a few bytes
2465 Roo.dd.DDM = Roo.dd.DragDropMgr;
2466 Roo.dd.DDM._addListeners();
2467
2468 }/*
2469  * Based on:
2470  * Ext JS Library 1.1.1
2471  * Copyright(c) 2006-2007, Ext JS, LLC.
2472  *
2473  * Originally Released Under LGPL - original licence link has changed is not relivant.
2474  *
2475  * Fork - LGPL
2476  * <script type="text/javascript">
2477  */
2478
2479 /**
2480  * @class Roo.dd.DD
2481  * A DragDrop implementation where the linked element follows the
2482  * mouse cursor during a drag.
2483  * @extends Roo.dd.DragDrop
2484  * @constructor
2485  * @param {String} id the id of the linked element
2486  * @param {String} sGroup the group of related DragDrop items
2487  * @param {object} config an object containing configurable attributes
2488  *                Valid properties for DD:
2489  *                    scroll
2490  */
2491 Roo.dd.DD = function(id, sGroup, config) {
2492     if (id) {
2493         this.init(id, sGroup, config);
2494     }
2495 };
2496
2497 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
2498
2499     /**
2500      * When set to true, the utility automatically tries to scroll the browser
2501      * window wehn a drag and drop element is dragged near the viewport boundary.
2502      * Defaults to true.
2503      * @property scroll
2504      * @type boolean
2505      */
2506     scroll: true,
2507
2508     /**
2509      * Sets the pointer offset to the distance between the linked element's top
2510      * left corner and the location the element was clicked
2511      * @method autoOffset
2512      * @param {int} iPageX the X coordinate of the click
2513      * @param {int} iPageY the Y coordinate of the click
2514      */
2515     autoOffset: function(iPageX, iPageY) {
2516         var x = iPageX - this.startPageX;
2517         var y = iPageY - this.startPageY;
2518         this.setDelta(x, y);
2519     },
2520
2521     /**
2522      * Sets the pointer offset.  You can call this directly to force the
2523      * offset to be in a particular location (e.g., pass in 0,0 to set it
2524      * to the center of the object)
2525      * @method setDelta
2526      * @param {int} iDeltaX the distance from the left
2527      * @param {int} iDeltaY the distance from the top
2528      */
2529     setDelta: function(iDeltaX, iDeltaY) {
2530         this.deltaX = iDeltaX;
2531         this.deltaY = iDeltaY;
2532     },
2533
2534     /**
2535      * Sets the drag element to the location of the mousedown or click event,
2536      * maintaining the cursor location relative to the location on the element
2537      * that was clicked.  Override this if you want to place the element in a
2538      * location other than where the cursor is.
2539      * @method setDragElPos
2540      * @param {int} iPageX the X coordinate of the mousedown or drag event
2541      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2542      */
2543     setDragElPos: function(iPageX, iPageY) {
2544         // the first time we do this, we are going to check to make sure
2545         // the element has css positioning
2546
2547         var el = this.getDragEl();
2548         this.alignElWithMouse(el, iPageX, iPageY);
2549     },
2550
2551     /**
2552      * Sets the element to the location of the mousedown or click event,
2553      * maintaining the cursor location relative to the location on the element
2554      * that was clicked.  Override this if you want to place the element in a
2555      * location other than where the cursor is.
2556      * @method alignElWithMouse
2557      * @param {HTMLElement} el the element to move
2558      * @param {int} iPageX the X coordinate of the mousedown or drag event
2559      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2560      */
2561     alignElWithMouse: function(el, iPageX, iPageY) {
2562         var oCoord = this.getTargetCoord(iPageX, iPageY);
2563         var fly = el.dom ? el : Roo.fly(el);
2564         if (!this.deltaSetXY) {
2565             var aCoord = [oCoord.x, oCoord.y];
2566             fly.setXY(aCoord);
2567             var newLeft = fly.getLeft(true);
2568             var newTop  = fly.getTop(true);
2569             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
2570         } else {
2571             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
2572         }
2573
2574         this.cachePosition(oCoord.x, oCoord.y);
2575         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
2576         return oCoord;
2577     },
2578
2579     /**
2580      * Saves the most recent position so that we can reset the constraints and
2581      * tick marks on-demand.  We need to know this so that we can calculate the
2582      * number of pixels the element is offset from its original position.
2583      * @method cachePosition
2584      * @param iPageX the current x position (optional, this just makes it so we
2585      * don't have to look it up again)
2586      * @param iPageY the current y position (optional, this just makes it so we
2587      * don't have to look it up again)
2588      */
2589     cachePosition: function(iPageX, iPageY) {
2590         if (iPageX) {
2591             this.lastPageX = iPageX;
2592             this.lastPageY = iPageY;
2593         } else {
2594             var aCoord = Roo.lib.Dom.getXY(this.getEl());
2595             this.lastPageX = aCoord[0];
2596             this.lastPageY = aCoord[1];
2597         }
2598     },
2599
2600     /**
2601      * Auto-scroll the window if the dragged object has been moved beyond the
2602      * visible window boundary.
2603      * @method autoScroll
2604      * @param {int} x the drag element's x position
2605      * @param {int} y the drag element's y position
2606      * @param {int} h the height of the drag element
2607      * @param {int} w the width of the drag element
2608      * @private
2609      */
2610     autoScroll: function(x, y, h, w) {
2611
2612         if (this.scroll) {
2613             // The client height
2614             var clientH = Roo.lib.Dom.getViewWidth();
2615
2616             // The client width
2617             var clientW = Roo.lib.Dom.getViewHeight();
2618
2619             // The amt scrolled down
2620             var st = this.DDM.getScrollTop();
2621
2622             // The amt scrolled right
2623             var sl = this.DDM.getScrollLeft();
2624
2625             // Location of the bottom of the element
2626             var bot = h + y;
2627
2628             // Location of the right of the element
2629             var right = w + x;
2630
2631             // The distance from the cursor to the bottom of the visible area,
2632             // adjusted so that we don't scroll if the cursor is beyond the
2633             // element drag constraints
2634             var toBot = (clientH + st - y - this.deltaY);
2635
2636             // The distance from the cursor to the right of the visible area
2637             var toRight = (clientW + sl - x - this.deltaX);
2638
2639
2640             // How close to the edge the cursor must be before we scroll
2641             // var thresh = (document.all) ? 100 : 40;
2642             var thresh = 40;
2643
2644             // How many pixels to scroll per autoscroll op.  This helps to reduce
2645             // clunky scrolling. IE is more sensitive about this ... it needs this
2646             // value to be higher.
2647             var scrAmt = (document.all) ? 80 : 30;
2648
2649             // Scroll down if we are near the bottom of the visible page and the
2650             // obj extends below the crease
2651             if ( bot > clientH && toBot < thresh ) {
2652                 window.scrollTo(sl, st + scrAmt);
2653             }
2654
2655             // Scroll up if the window is scrolled down and the top of the object
2656             // goes above the top border
2657             if ( y < st && st > 0 && y - st < thresh ) {
2658                 window.scrollTo(sl, st - scrAmt);
2659             }
2660
2661             // Scroll right if the obj is beyond the right border and the cursor is
2662             // near the border.
2663             if ( right > clientW && toRight < thresh ) {
2664                 window.scrollTo(sl + scrAmt, st);
2665             }
2666
2667             // Scroll left if the window has been scrolled to the right and the obj
2668             // extends past the left border
2669             if ( x < sl && sl > 0 && x - sl < thresh ) {
2670                 window.scrollTo(sl - scrAmt, st);
2671             }
2672         }
2673     },
2674
2675     /**
2676      * Finds the location the element should be placed if we want to move
2677      * it to where the mouse location less the click offset would place us.
2678      * @method getTargetCoord
2679      * @param {int} iPageX the X coordinate of the click
2680      * @param {int} iPageY the Y coordinate of the click
2681      * @return an object that contains the coordinates (Object.x and Object.y)
2682      * @private
2683      */
2684     getTargetCoord: function(iPageX, iPageY) {
2685
2686
2687         var x = iPageX - this.deltaX;
2688         var y = iPageY - this.deltaY;
2689
2690         if (this.constrainX) {
2691             if (x < this.minX) { x = this.minX; }
2692             if (x > this.maxX) { x = this.maxX; }
2693         }
2694
2695         if (this.constrainY) {
2696             if (y < this.minY) { y = this.minY; }
2697             if (y > this.maxY) { y = this.maxY; }
2698         }
2699
2700         x = this.getTick(x, this.xTicks);
2701         y = this.getTick(y, this.yTicks);
2702
2703
2704         return {x:x, y:y};
2705     },
2706
2707     /*
2708      * Sets up config options specific to this class. Overrides
2709      * Roo.dd.DragDrop, but all versions of this method through the
2710      * inheritance chain are called
2711      */
2712     applyConfig: function() {
2713         Roo.dd.DD.superclass.applyConfig.call(this);
2714         this.scroll = (this.config.scroll !== false);
2715     },
2716
2717     /*
2718      * Event that fires prior to the onMouseDown event.  Overrides
2719      * Roo.dd.DragDrop.
2720      */
2721     b4MouseDown: function(e) {
2722         // this.resetConstraints();
2723         this.autoOffset(e.getPageX(),
2724                             e.getPageY());
2725     },
2726
2727     /*
2728      * Event that fires prior to the onDrag event.  Overrides
2729      * Roo.dd.DragDrop.
2730      */
2731     b4Drag: function(e) {
2732         this.setDragElPos(e.getPageX(),
2733                             e.getPageY());
2734     },
2735
2736     toString: function() {
2737         return ("DD " + this.id);
2738     }
2739
2740     //////////////////////////////////////////////////////////////////////////
2741     // Debugging ygDragDrop events that can be overridden
2742     //////////////////////////////////////////////////////////////////////////
2743     /*
2744     startDrag: function(x, y) {
2745     },
2746
2747     onDrag: function(e) {
2748     },
2749
2750     onDragEnter: function(e, id) {
2751     },
2752
2753     onDragOver: function(e, id) {
2754     },
2755
2756     onDragOut: function(e, id) {
2757     },
2758
2759     onDragDrop: function(e, id) {
2760     },
2761
2762     endDrag: function(e) {
2763     }
2764
2765     */
2766
2767 });/*
2768  * Based on:
2769  * Ext JS Library 1.1.1
2770  * Copyright(c) 2006-2007, Ext JS, LLC.
2771  *
2772  * Originally Released Under LGPL - original licence link has changed is not relivant.
2773  *
2774  * Fork - LGPL
2775  * <script type="text/javascript">
2776  */
2777
2778 /**
2779  * @class Roo.dd.DDProxy
2780  * A DragDrop implementation that inserts an empty, bordered div into
2781  * the document that follows the cursor during drag operations.  At the time of
2782  * the click, the frame div is resized to the dimensions of the linked html
2783  * element, and moved to the exact location of the linked element.
2784  *
2785  * References to the "frame" element refer to the single proxy element that
2786  * was created to be dragged in place of all DDProxy elements on the
2787  * page.
2788  *
2789  * @extends Roo.dd.DD
2790  * @constructor
2791  * @param {String} id the id of the linked html element
2792  * @param {String} sGroup the group of related DragDrop objects
2793  * @param {object} config an object containing configurable attributes
2794  *                Valid properties for DDProxy in addition to those in DragDrop:
2795  *                   resizeFrame, centerFrame, dragElId
2796  */
2797 Roo.dd.DDProxy = function(id, sGroup, config) {
2798     if (id) {
2799         this.init(id, sGroup, config);
2800         this.initFrame();
2801     }
2802 };
2803
2804 /**
2805  * The default drag frame div id
2806  * @property Roo.dd.DDProxy.dragElId
2807  * @type String
2808  * @static
2809  */
2810 Roo.dd.DDProxy.dragElId = "ygddfdiv";
2811
2812 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
2813
2814     /**
2815      * By default we resize the drag frame to be the same size as the element
2816      * we want to drag (this is to get the frame effect).  We can turn it off
2817      * if we want a different behavior.
2818      * @property resizeFrame
2819      * @type boolean
2820      */
2821     resizeFrame: true,
2822
2823     /**
2824      * By default the frame is positioned exactly where the drag element is, so
2825      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
2826      * you do not have constraints on the obj is to have the drag frame centered
2827      * around the cursor.  Set centerFrame to true for this effect.
2828      * @property centerFrame
2829      * @type boolean
2830      */
2831     centerFrame: false,
2832
2833     /**
2834      * Creates the proxy element if it does not yet exist
2835      * @method createFrame
2836      */
2837     createFrame: function() {
2838         var self = this;
2839         var body = document.body;
2840
2841         if (!body || !body.firstChild) {
2842             setTimeout( function() { self.createFrame(); }, 50 );
2843             return;
2844         }
2845
2846         var div = this.getDragEl();
2847
2848         if (!div) {
2849             div    = document.createElement("div");
2850             div.id = this.dragElId;
2851             var s  = div.style;
2852
2853             s.position   = "absolute";
2854             s.visibility = "hidden";
2855             s.cursor     = "move";
2856             s.border     = "2px solid #aaa";
2857             s.zIndex     = 999;
2858
2859             // appendChild can blow up IE if invoked prior to the window load event
2860             // while rendering a table.  It is possible there are other scenarios
2861             // that would cause this to happen as well.
2862             body.insertBefore(div, body.firstChild);
2863         }
2864     },
2865
2866     /**
2867      * Initialization for the drag frame element.  Must be called in the
2868      * constructor of all subclasses
2869      * @method initFrame
2870      */
2871     initFrame: function() {
2872         this.createFrame();
2873     },
2874
2875     applyConfig: function() {
2876         Roo.dd.DDProxy.superclass.applyConfig.call(this);
2877
2878         this.resizeFrame = (this.config.resizeFrame !== false);
2879         this.centerFrame = (this.config.centerFrame);
2880         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
2881     },
2882
2883     /**
2884      * Resizes the drag frame to the dimensions of the clicked object, positions
2885      * it over the object, and finally displays it
2886      * @method showFrame
2887      * @param {int} iPageX X click position
2888      * @param {int} iPageY Y click position
2889      * @private
2890      */
2891     showFrame: function(iPageX, iPageY) {
2892         var el = this.getEl();
2893         var dragEl = this.getDragEl();
2894         var s = dragEl.style;
2895
2896         this._resizeProxy();
2897
2898         if (this.centerFrame) {
2899             this.setDelta( Math.round(parseInt(s.width,  10)/2),
2900                            Math.round(parseInt(s.height, 10)/2) );
2901         }
2902
2903         this.setDragElPos(iPageX, iPageY);
2904
2905         Roo.fly(dragEl).show();
2906     },
2907
2908     /**
2909      * The proxy is automatically resized to the dimensions of the linked
2910      * element when a drag is initiated, unless resizeFrame is set to false
2911      * @method _resizeProxy
2912      * @private
2913      */
2914     _resizeProxy: function() {
2915         if (this.resizeFrame) {
2916             var el = this.getEl();
2917             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
2918         }
2919     },
2920
2921     // overrides Roo.dd.DragDrop
2922     b4MouseDown: function(e) {
2923         var x = e.getPageX();
2924         var y = e.getPageY();
2925         this.autoOffset(x, y);
2926         this.setDragElPos(x, y);
2927     },
2928
2929     // overrides Roo.dd.DragDrop
2930     b4StartDrag: function(x, y) {
2931         // show the drag frame
2932         this.showFrame(x, y);
2933     },
2934
2935     // overrides Roo.dd.DragDrop
2936     b4EndDrag: function(e) {
2937         Roo.fly(this.getDragEl()).hide();
2938     },
2939
2940     // overrides Roo.dd.DragDrop
2941     // By default we try to move the element to the last location of the frame.
2942     // This is so that the default behavior mirrors that of Roo.dd.DD.
2943     endDrag: function(e) {
2944
2945         var lel = this.getEl();
2946         var del = this.getDragEl();
2947
2948         // Show the drag frame briefly so we can get its position
2949         del.style.visibility = "";
2950
2951         this.beforeMove();
2952         // Hide the linked element before the move to get around a Safari
2953         // rendering bug.
2954         lel.style.visibility = "hidden";
2955         Roo.dd.DDM.moveToEl(lel, del);
2956         del.style.visibility = "hidden";
2957         lel.style.visibility = "";
2958
2959         this.afterDrag();
2960     },
2961
2962     beforeMove : function(){
2963
2964     },
2965
2966     afterDrag : function(){
2967
2968     },
2969
2970     toString: function() {
2971         return ("DDProxy " + this.id);
2972     }
2973
2974 });
2975 /*
2976  * Based on:
2977  * Ext JS Library 1.1.1
2978  * Copyright(c) 2006-2007, Ext JS, LLC.
2979  *
2980  * Originally Released Under LGPL - original licence link has changed is not relivant.
2981  *
2982  * Fork - LGPL
2983  * <script type="text/javascript">
2984  */
2985
2986  /**
2987  * @class Roo.dd.DDTarget
2988  * A DragDrop implementation that does not move, but can be a drop
2989  * target.  You would get the same result by simply omitting implementation
2990  * for the event callbacks, but this way we reduce the processing cost of the
2991  * event listener and the callbacks.
2992  * @extends Roo.dd.DragDrop
2993  * @constructor
2994  * @param {String} id the id of the element that is a drop target
2995  * @param {String} sGroup the group of related DragDrop objects
2996  * @param {object} config an object containing configurable attributes
2997  *                 Valid properties for DDTarget in addition to those in
2998  *                 DragDrop:
2999  *                    none
3000  */
3001 Roo.dd.DDTarget = function(id, sGroup, config) {
3002     if (id) {
3003         this.initTarget(id, sGroup, config);
3004     }
3005     if (config.listeners || config.events) { 
3006        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
3007             listeners : config.listeners || {}, 
3008             events : config.events || {} 
3009         });    
3010     }
3011 };
3012
3013 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
3014 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
3015     toString: function() {
3016         return ("DDTarget " + this.id);
3017     }
3018 });
3019 /*
3020  * Based on:
3021  * Ext JS Library 1.1.1
3022  * Copyright(c) 2006-2007, Ext JS, LLC.
3023  *
3024  * Originally Released Under LGPL - original licence link has changed is not relivant.
3025  *
3026  * Fork - LGPL
3027  * <script type="text/javascript">
3028  */
3029  
3030
3031 /**
3032  * @class Roo.dd.ScrollManager
3033  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
3034  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
3035  * @singleton
3036  */
3037 Roo.dd.ScrollManager = function(){
3038     var ddm = Roo.dd.DragDropMgr;
3039     var els = {};
3040     var dragEl = null;
3041     var proc = {};
3042     
3043     
3044     
3045     var onStop = function(e){
3046         dragEl = null;
3047         clearProc();
3048     };
3049     
3050     var triggerRefresh = function(){
3051         if(ddm.dragCurrent){
3052              ddm.refreshCache(ddm.dragCurrent.groups);
3053         }
3054     };
3055     
3056     var doScroll = function(){
3057         if(ddm.dragCurrent){
3058             var dds = Roo.dd.ScrollManager;
3059             if(!dds.animate){
3060                 if(proc.el.scroll(proc.dir, dds.increment)){
3061                     triggerRefresh();
3062                 }
3063             }else{
3064                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
3065             }
3066         }
3067     };
3068     
3069     var clearProc = function(){
3070         if(proc.id){
3071             clearInterval(proc.id);
3072         }
3073         proc.id = 0;
3074         proc.el = null;
3075         proc.dir = "";
3076     };
3077     
3078     var startProc = function(el, dir){
3079          Roo.log('scroll startproc');
3080         clearProc();
3081         proc.el = el;
3082         proc.dir = dir;
3083         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
3084     };
3085     
3086     var onFire = function(e, isDrop){
3087        
3088         if(isDrop || !ddm.dragCurrent){ return; }
3089         var dds = Roo.dd.ScrollManager;
3090         if(!dragEl || dragEl != ddm.dragCurrent){
3091             dragEl = ddm.dragCurrent;
3092             // refresh regions on drag start
3093             dds.refreshCache();
3094         }
3095         
3096         var xy = Roo.lib.Event.getXY(e);
3097         var pt = new Roo.lib.Point(xy[0], xy[1]);
3098         for(var id in els){
3099             var el = els[id], r = el._region;
3100             if(r && r.contains(pt) && el.isScrollable()){
3101                 if(r.bottom - pt.y <= dds.thresh){
3102                     if(proc.el != el){
3103                         startProc(el, "down");
3104                     }
3105                     return;
3106                 }else if(r.right - pt.x <= dds.thresh){
3107                     if(proc.el != el){
3108                         startProc(el, "left");
3109                     }
3110                     return;
3111                 }else if(pt.y - r.top <= dds.thresh){
3112                     if(proc.el != el){
3113                         startProc(el, "up");
3114                     }
3115                     return;
3116                 }else if(pt.x - r.left <= dds.thresh){
3117                     if(proc.el != el){
3118                         startProc(el, "right");
3119                     }
3120                     return;
3121                 }
3122             }
3123         }
3124         clearProc();
3125     };
3126     
3127     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
3128     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
3129     
3130     return {
3131         /**
3132          * Registers new overflow element(s) to auto scroll
3133          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
3134          */
3135         register : function(el){
3136             if(el instanceof Array){
3137                 for(var i = 0, len = el.length; i < len; i++) {
3138                         this.register(el[i]);
3139                 }
3140             }else{
3141                 el = Roo.get(el);
3142                 els[el.id] = el;
3143             }
3144             Roo.dd.ScrollManager.els = els;
3145         },
3146         
3147         /**
3148          * Unregisters overflow element(s) so they are no longer scrolled
3149          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
3150          */
3151         unregister : function(el){
3152             if(el instanceof Array){
3153                 for(var i = 0, len = el.length; i < len; i++) {
3154                         this.unregister(el[i]);
3155                 }
3156             }else{
3157                 el = Roo.get(el);
3158                 delete els[el.id];
3159             }
3160         },
3161         
3162         /**
3163          * The number of pixels from the edge of a container the pointer needs to be to 
3164          * trigger scrolling (defaults to 25)
3165          * @type Number
3166          */
3167         thresh : 25,
3168         
3169         /**
3170          * The number of pixels to scroll in each scroll increment (defaults to 50)
3171          * @type Number
3172          */
3173         increment : 100,
3174         
3175         /**
3176          * The frequency of scrolls in milliseconds (defaults to 500)
3177          * @type Number
3178          */
3179         frequency : 500,
3180         
3181         /**
3182          * True to animate the scroll (defaults to true)
3183          * @type Boolean
3184          */
3185         animate: true,
3186         
3187         /**
3188          * The animation duration in seconds - 
3189          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
3190          * @type Number
3191          */
3192         animDuration: .4,
3193         
3194         /**
3195          * Manually trigger a cache refresh.
3196          */
3197         refreshCache : function(){
3198             for(var id in els){
3199                 if(typeof els[id] == 'object'){ // for people extending the object prototype
3200                     els[id]._region = els[id].getRegion();
3201                 }
3202             }
3203         }
3204     };
3205 }();/*
3206  * Based on:
3207  * Ext JS Library 1.1.1
3208  * Copyright(c) 2006-2007, Ext JS, LLC.
3209  *
3210  * Originally Released Under LGPL - original licence link has changed is not relivant.
3211  *
3212  * Fork - LGPL
3213  * <script type="text/javascript">
3214  */
3215  
3216
3217 /**
3218  * @class Roo.dd.Registry
3219  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
3220  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
3221  * @singleton
3222  */
3223 Roo.dd.Registry = function(){
3224     var elements = {}; 
3225     var handles = {}; 
3226     var autoIdSeed = 0;
3227
3228     var getId = function(el, autogen){
3229         if(typeof el == "string"){
3230             return el;
3231         }
3232         var id = el.id;
3233         if(!id && autogen !== false){
3234             id = "roodd-" + (++autoIdSeed);
3235             el.id = id;
3236         }
3237         return id;
3238     };
3239     
3240     return {
3241     /**
3242      * Register a drag drop element
3243      * @param {String|HTMLElement} element The id or DOM node to register
3244      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
3245      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
3246      * knows how to interpret, plus there are some specific properties known to the Registry that should be
3247      * populated in the data object (if applicable):
3248      * <pre>
3249 Value      Description<br />
3250 ---------  ------------------------------------------<br />
3251 handles    Array of DOM nodes that trigger dragging<br />
3252            for the element being registered<br />
3253 isHandle   True if the element passed in triggers<br />
3254            dragging itself, else false
3255 </pre>
3256      */
3257         register : function(el, data){
3258             data = data || {};
3259             if(typeof el == "string"){
3260                 el = document.getElementById(el);
3261             }
3262             data.ddel = el;
3263             elements[getId(el)] = data;
3264             if(data.isHandle !== false){
3265                 handles[data.ddel.id] = data;
3266             }
3267             if(data.handles){
3268                 var hs = data.handles;
3269                 for(var i = 0, len = hs.length; i < len; i++){
3270                         handles[getId(hs[i])] = data;
3271                 }
3272             }
3273         },
3274
3275     /**
3276      * Unregister a drag drop element
3277      * @param {String|HTMLElement}  element The id or DOM node to unregister
3278      */
3279         unregister : function(el){
3280             var id = getId(el, false);
3281             var data = elements[id];
3282             if(data){
3283                 delete elements[id];
3284                 if(data.handles){
3285                     var hs = data.handles;
3286                     for(var i = 0, len = hs.length; i < len; i++){
3287                         delete handles[getId(hs[i], false)];
3288                     }
3289                 }
3290             }
3291         },
3292
3293     /**
3294      * Returns the handle registered for a DOM Node by id
3295      * @param {String|HTMLElement} id The DOM node or id to look up
3296      * @return {Object} handle The custom handle data
3297      */
3298         getHandle : function(id){
3299             if(typeof id != "string"){ // must be element?
3300                 id = id.id;
3301             }
3302             return handles[id];
3303         },
3304
3305     /**
3306      * Returns the handle that is registered for the DOM node that is the target of the event
3307      * @param {Event} e The event
3308      * @return {Object} handle The custom handle data
3309      */
3310         getHandleFromEvent : function(e){
3311             var t = Roo.lib.Event.getTarget(e);
3312             return t ? handles[t.id] : null;
3313         },
3314
3315     /**
3316      * Returns a custom data object that is registered for a DOM node by id
3317      * @param {String|HTMLElement} id The DOM node or id to look up
3318      * @return {Object} data The custom data
3319      */
3320         getTarget : function(id){
3321             if(typeof id != "string"){ // must be element?
3322                 id = id.id;
3323             }
3324             return elements[id];
3325         },
3326
3327     /**
3328      * Returns a custom data object that is registered for the DOM node that is the target of the event
3329      * @param {Event} e The event
3330      * @return {Object} data The custom data
3331      */
3332         getTargetFromEvent : function(e){
3333             var t = Roo.lib.Event.getTarget(e);
3334             return t ? elements[t.id] || handles[t.id] : null;
3335         }
3336     };
3337 }();/*
3338  * Based on:
3339  * Ext JS Library 1.1.1
3340  * Copyright(c) 2006-2007, Ext JS, LLC.
3341  *
3342  * Originally Released Under LGPL - original licence link has changed is not relivant.
3343  *
3344  * Fork - LGPL
3345  * <script type="text/javascript">
3346  */
3347  
3348
3349 /**
3350  * @class Roo.dd.StatusProxy
3351  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
3352  * default drag proxy used by all Roo.dd components.
3353  * @constructor
3354  * @param {Object} config
3355  */
3356 Roo.dd.StatusProxy = function(config){
3357     Roo.apply(this, config);
3358     this.id = this.id || Roo.id();
3359     this.el = new Roo.Layer({
3360         dh: {
3361             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
3362                 {tag: "div", cls: "x-dd-drop-icon"},
3363                 {tag: "div", cls: "x-dd-drag-ghost"}
3364             ]
3365         }, 
3366         shadow: !config || config.shadow !== false
3367     });
3368     this.ghost = Roo.get(this.el.dom.childNodes[1]);
3369     this.dropStatus = this.dropNotAllowed;
3370 };
3371
3372 Roo.dd.StatusProxy.prototype = {
3373     /**
3374      * @cfg {String} dropAllowed
3375      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
3376      */
3377     dropAllowed : "x-dd-drop-ok",
3378     /**
3379      * @cfg {String} dropNotAllowed
3380      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
3381      */
3382     dropNotAllowed : "x-dd-drop-nodrop",
3383
3384     /**
3385      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
3386      * over the current target element.
3387      * @param {String} cssClass The css class for the new drop status indicator image
3388      */
3389     setStatus : function(cssClass){
3390         cssClass = cssClass || this.dropNotAllowed;
3391         if(this.dropStatus != cssClass){
3392             this.el.replaceClass(this.dropStatus, cssClass);
3393             this.dropStatus = cssClass;
3394         }
3395     },
3396
3397     /**
3398      * Resets the status indicator to the default dropNotAllowed value
3399      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
3400      */
3401     reset : function(clearGhost){
3402         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
3403         this.dropStatus = this.dropNotAllowed;
3404         if(clearGhost){
3405             this.ghost.update("");
3406         }
3407     },
3408
3409     /**
3410      * Updates the contents of the ghost element
3411      * @param {String} html The html that will replace the current innerHTML of the ghost element
3412      */
3413     update : function(html){
3414         if(typeof html == "string"){
3415             this.ghost.update(html);
3416         }else{
3417             this.ghost.update("");
3418             html.style.margin = "0";
3419             this.ghost.dom.appendChild(html);
3420         }
3421         // ensure float = none set?? cant remember why though.
3422         var el = this.ghost.dom.firstChild;
3423                 if(el){
3424                         Roo.fly(el).setStyle('float', 'none');
3425                 }
3426     },
3427     
3428     /**
3429      * Returns the underlying proxy {@link Roo.Layer}
3430      * @return {Roo.Layer} el
3431     */
3432     getEl : function(){
3433         return this.el;
3434     },
3435
3436     /**
3437      * Returns the ghost element
3438      * @return {Roo.Element} el
3439      */
3440     getGhost : function(){
3441         return this.ghost;
3442     },
3443
3444     /**
3445      * Hides the proxy
3446      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
3447      */
3448     hide : function(clear){
3449         this.el.hide();
3450         if(clear){
3451             this.reset(true);
3452         }
3453     },
3454
3455     /**
3456      * Stops the repair animation if it's currently running
3457      */
3458     stop : function(){
3459         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
3460             this.anim.stop();
3461         }
3462     },
3463
3464     /**
3465      * Displays this proxy
3466      */
3467     show : function(){
3468         this.el.show();
3469     },
3470
3471     /**
3472      * Force the Layer to sync its shadow and shim positions to the element
3473      */
3474     sync : function(){
3475         this.el.sync();
3476     },
3477
3478     /**
3479      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
3480      * invalid drop operation by the item being dragged.
3481      * @param {Array} xy The XY position of the element ([x, y])
3482      * @param {Function} callback The function to call after the repair is complete
3483      * @param {Object} scope The scope in which to execute the callback
3484      */
3485     repair : function(xy, callback, scope){
3486         this.callback = callback;
3487         this.scope = scope;
3488         if(xy && this.animRepair !== false){
3489             this.el.addClass("x-dd-drag-repair");
3490             this.el.hideUnders(true);
3491             this.anim = this.el.shift({
3492                 duration: this.repairDuration || .5,
3493                 easing: 'easeOut',
3494                 xy: xy,
3495                 stopFx: true,
3496                 callback: this.afterRepair,
3497                 scope: this
3498             });
3499         }else{
3500             this.afterRepair();
3501         }
3502     },
3503
3504     // private
3505     afterRepair : function(){
3506         this.hide(true);
3507         if(typeof this.callback == "function"){
3508             this.callback.call(this.scope || this);
3509         }
3510         this.callback = null;
3511         this.scope = null;
3512     }
3513 };/*
3514  * Based on:
3515  * Ext JS Library 1.1.1
3516  * Copyright(c) 2006-2007, Ext JS, LLC.
3517  *
3518  * Originally Released Under LGPL - original licence link has changed is not relivant.
3519  *
3520  * Fork - LGPL
3521  * <script type="text/javascript">
3522  */
3523
3524 /**
3525  * @class Roo.dd.DragSource
3526  * @extends Roo.dd.DDProxy
3527  * A simple class that provides the basic implementation needed to make any element draggable.
3528  * @constructor
3529  * @param {String/HTMLElement/Element} el The container element
3530  * @param {Object} config
3531  */
3532 Roo.dd.DragSource = function(el, config){
3533     this.el = Roo.get(el);
3534     this.dragData = {};
3535     
3536     Roo.apply(this, config);
3537     
3538     if(!this.proxy){
3539         this.proxy = new Roo.dd.StatusProxy();
3540     }
3541
3542     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
3543           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
3544     
3545     this.dragging = false;
3546 };
3547
3548 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
3549     /**
3550      * @cfg {String} dropAllowed
3551      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3552      */
3553     dropAllowed : "x-dd-drop-ok",
3554     /**
3555      * @cfg {String} dropNotAllowed
3556      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3557      */
3558     dropNotAllowed : "x-dd-drop-nodrop",
3559
3560     /**
3561      * Returns the data object associated with this drag source
3562      * @return {Object} data An object containing arbitrary data
3563      */
3564     getDragData : function(e){
3565         return this.dragData;
3566     },
3567
3568     // private
3569     onDragEnter : function(e, id){
3570         var target = Roo.dd.DragDropMgr.getDDById(id);
3571         this.cachedTarget = target;
3572         if(this.beforeDragEnter(target, e, id) !== false){
3573             if(target.isNotifyTarget){
3574                 var status = target.notifyEnter(this, e, this.dragData);
3575                 this.proxy.setStatus(status);
3576             }else{
3577                 this.proxy.setStatus(this.dropAllowed);
3578             }
3579             
3580             if(this.afterDragEnter){
3581                 /**
3582                  * An empty function by default, but provided so that you can perform a custom action
3583                  * when the dragged item enters the drop target by providing an implementation.
3584                  * @param {Roo.dd.DragDrop} target The drop target
3585                  * @param {Event} e The event object
3586                  * @param {String} id The id of the dragged element
3587                  * @method afterDragEnter
3588                  */
3589                 this.afterDragEnter(target, e, id);
3590             }
3591         }
3592     },
3593
3594     /**
3595      * An empty function by default, but provided so that you can perform a custom action
3596      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
3597      * @param {Roo.dd.DragDrop} target The drop target
3598      * @param {Event} e The event object
3599      * @param {String} id The id of the dragged element
3600      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3601      */
3602     beforeDragEnter : function(target, e, id){
3603         return true;
3604     },
3605
3606     // private
3607     alignElWithMouse: function() {
3608         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
3609         this.proxy.sync();
3610     },
3611
3612     // private
3613     onDragOver : function(e, id){
3614         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3615         if(this.beforeDragOver(target, e, id) !== false){
3616             if(target.isNotifyTarget){
3617                 var status = target.notifyOver(this, e, this.dragData);
3618                 this.proxy.setStatus(status);
3619             }
3620
3621             if(this.afterDragOver){
3622                 /**
3623                  * An empty function by default, but provided so that you can perform a custom action
3624                  * while the dragged item is over the drop target by providing an implementation.
3625                  * @param {Roo.dd.DragDrop} target The drop target
3626                  * @param {Event} e The event object
3627                  * @param {String} id The id of the dragged element
3628                  * @method afterDragOver
3629                  */
3630                 this.afterDragOver(target, e, id);
3631             }
3632         }
3633     },
3634
3635     /**
3636      * An empty function by default, but provided so that you can perform a custom action
3637      * while the dragged item is over the drop target and optionally cancel the onDragOver.
3638      * @param {Roo.dd.DragDrop} target The drop target
3639      * @param {Event} e The event object
3640      * @param {String} id The id of the dragged element
3641      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3642      */
3643     beforeDragOver : function(target, e, id){
3644         return true;
3645     },
3646
3647     // private
3648     onDragOut : function(e, id){
3649         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3650         if(this.beforeDragOut(target, e, id) !== false){
3651             if(target.isNotifyTarget){
3652                 target.notifyOut(this, e, this.dragData);
3653             }
3654             this.proxy.reset();
3655             if(this.afterDragOut){
3656                 /**
3657                  * An empty function by default, but provided so that you can perform a custom action
3658                  * after the dragged item is dragged out of the target without dropping.
3659                  * @param {Roo.dd.DragDrop} target The drop target
3660                  * @param {Event} e The event object
3661                  * @param {String} id The id of the dragged element
3662                  * @method afterDragOut
3663                  */
3664                 this.afterDragOut(target, e, id);
3665             }
3666         }
3667         this.cachedTarget = null;
3668     },
3669
3670     /**
3671      * An empty function by default, but provided so that you can perform a custom action before the dragged
3672      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
3673      * @param {Roo.dd.DragDrop} target The drop target
3674      * @param {Event} e The event object
3675      * @param {String} id The id of the dragged element
3676      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3677      */
3678     beforeDragOut : function(target, e, id){
3679         return true;
3680     },
3681     
3682     // private
3683     onDragDrop : function(e, id){
3684         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3685         if(this.beforeDragDrop(target, e, id) !== false){
3686             if(target.isNotifyTarget){
3687                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
3688                     this.onValidDrop(target, e, id);
3689                 }else{
3690                     this.onInvalidDrop(target, e, id);
3691                 }
3692             }else{
3693                 this.onValidDrop(target, e, id);
3694             }
3695             
3696             if(this.afterDragDrop){
3697                 /**
3698                  * An empty function by default, but provided so that you can perform a custom action
3699                  * after a valid drag drop has occurred by providing an implementation.
3700                  * @param {Roo.dd.DragDrop} target The drop target
3701                  * @param {Event} e The event object
3702                  * @param {String} id The id of the dropped element
3703                  * @method afterDragDrop
3704                  */
3705                 this.afterDragDrop(target, e, id);
3706             }
3707         }
3708         delete this.cachedTarget;
3709     },
3710
3711     /**
3712      * An empty function by default, but provided so that you can perform a custom action before the dragged
3713      * item is dropped onto the target and optionally cancel the onDragDrop.
3714      * @param {Roo.dd.DragDrop} target The drop target
3715      * @param {Event} e The event object
3716      * @param {String} id The id of the dragged element
3717      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
3718      */
3719     beforeDragDrop : function(target, e, id){
3720         return true;
3721     },
3722
3723     // private
3724     onValidDrop : function(target, e, id){
3725         this.hideProxy();
3726         if(this.afterValidDrop){
3727             /**
3728              * An empty function by default, but provided so that you can perform a custom action
3729              * after a valid drop has occurred by providing an implementation.
3730              * @param {Object} target The target DD 
3731              * @param {Event} e The event object
3732              * @param {String} id The id of the dropped element
3733              * @method afterInvalidDrop
3734              */
3735             this.afterValidDrop(target, e, id);
3736         }
3737     },
3738
3739     // private
3740     getRepairXY : function(e, data){
3741         return this.el.getXY();  
3742     },
3743
3744     // private
3745     onInvalidDrop : function(target, e, id){
3746         this.beforeInvalidDrop(target, e, id);
3747         if(this.cachedTarget){
3748             if(this.cachedTarget.isNotifyTarget){
3749                 this.cachedTarget.notifyOut(this, e, this.dragData);
3750             }
3751             this.cacheTarget = null;
3752         }
3753         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
3754
3755         if(this.afterInvalidDrop){
3756             /**
3757              * An empty function by default, but provided so that you can perform a custom action
3758              * after an invalid drop has occurred by providing an implementation.
3759              * @param {Event} e The event object
3760              * @param {String} id The id of the dropped element
3761              * @method afterInvalidDrop
3762              */
3763             this.afterInvalidDrop(e, id);
3764         }
3765     },
3766
3767     // private
3768     afterRepair : function(){
3769         if(Roo.enableFx){
3770             this.el.highlight(this.hlColor || "c3daf9");
3771         }
3772         this.dragging = false;
3773     },
3774
3775     /**
3776      * An empty function by default, but provided so that you can perform a custom action after an invalid
3777      * drop has occurred.
3778      * @param {Roo.dd.DragDrop} target The drop target
3779      * @param {Event} e The event object
3780      * @param {String} id The id of the dragged element
3781      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
3782      */
3783     beforeInvalidDrop : function(target, e, id){
3784         return true;
3785     },
3786
3787     // private
3788     handleMouseDown : function(e){
3789         if(this.dragging) {
3790             return;
3791         }
3792         var data = this.getDragData(e);
3793         if(data && this.onBeforeDrag(data, e) !== false){
3794             this.dragData = data;
3795             this.proxy.stop();
3796             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
3797         } 
3798     },
3799
3800     /**
3801      * An empty function by default, but provided so that you can perform a custom action before the initial
3802      * drag event begins and optionally cancel it.
3803      * @param {Object} data An object containing arbitrary data to be shared with drop targets
3804      * @param {Event} e The event object
3805      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3806      */
3807     onBeforeDrag : function(data, e){
3808         return true;
3809     },
3810
3811     /**
3812      * An empty function by default, but provided so that you can perform a custom action once the initial
3813      * drag event has begun.  The drag cannot be canceled from this function.
3814      * @param {Number} x The x position of the click on the dragged object
3815      * @param {Number} y The y position of the click on the dragged object
3816      */
3817     onStartDrag : Roo.emptyFn,
3818
3819     // private - YUI override
3820     startDrag : function(x, y){
3821         this.proxy.reset();
3822         this.dragging = true;
3823         this.proxy.update("");
3824         this.onInitDrag(x, y);
3825         this.proxy.show();
3826     },
3827
3828     // private
3829     onInitDrag : function(x, y){
3830         var clone = this.el.dom.cloneNode(true);
3831         clone.id = Roo.id(); // prevent duplicate ids
3832         this.proxy.update(clone);
3833         this.onStartDrag(x, y);
3834         return true;
3835     },
3836
3837     /**
3838      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
3839      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
3840      */
3841     getProxy : function(){
3842         return this.proxy;  
3843     },
3844
3845     /**
3846      * Hides the drag source's {@link Roo.dd.StatusProxy}
3847      */
3848     hideProxy : function(){
3849         this.proxy.hide();  
3850         this.proxy.reset(true);
3851         this.dragging = false;
3852     },
3853
3854     // private
3855     triggerCacheRefresh : function(){
3856         Roo.dd.DDM.refreshCache(this.groups);
3857     },
3858
3859     // private - override to prevent hiding
3860     b4EndDrag: function(e) {
3861     },
3862
3863     // private - override to prevent moving
3864     endDrag : function(e){
3865         this.onEndDrag(this.dragData, e);
3866     },
3867
3868     // private
3869     onEndDrag : function(data, e){
3870     },
3871     
3872     // private - pin to cursor
3873     autoOffset : function(x, y) {
3874         this.setDelta(-12, -20);
3875     }    
3876 });/*
3877  * Based on:
3878  * Ext JS Library 1.1.1
3879  * Copyright(c) 2006-2007, Ext JS, LLC.
3880  *
3881  * Originally Released Under LGPL - original licence link has changed is not relivant.
3882  *
3883  * Fork - LGPL
3884  * <script type="text/javascript">
3885  */
3886
3887
3888 /**
3889  * @class Roo.dd.DropTarget
3890  * @extends Roo.dd.DDTarget
3891  * A simple class that provides the basic implementation needed to make any element a drop target that can have
3892  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
3893  * @constructor
3894  * @param {String/HTMLElement/Element} el The container element
3895  * @param {Object} config
3896  */
3897 Roo.dd.DropTarget = function(el, config){
3898     this.el = Roo.get(el);
3899     
3900     var listeners = false; ;
3901     if (config && config.listeners) {
3902         listeners= config.listeners;
3903         delete config.listeners;
3904     }
3905     Roo.apply(this, config);
3906     
3907     if(this.containerScroll){
3908         Roo.dd.ScrollManager.register(this.el);
3909     }
3910     this.addEvents( {
3911          /**
3912          * @scope Roo.dd.DropTarget
3913          */
3914          
3915          /**
3916          * @event enter
3917          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
3918          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
3919          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
3920          * 
3921          * IMPORTANT : it should set this.overClass and this.dropAllowed
3922          * 
3923          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3924          * @param {Event} e The event
3925          * @param {Object} data An object containing arbitrary data supplied by the drag source
3926          */
3927         "enter" : true,
3928         
3929          /**
3930          * @event over
3931          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
3932          * This method will be called on every mouse movement while the drag source is over the drop target.
3933          * This default implementation simply returns the dropAllowed config value.
3934          * 
3935          * IMPORTANT : it should set this.dropAllowed
3936          * 
3937          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3938          * @param {Event} e The event
3939          * @param {Object} data An object containing arbitrary data supplied by the drag source
3940          
3941          */
3942         "over" : true,
3943         /**
3944          * @event out
3945          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
3946          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
3947          * overClass (if any) from the drop element.
3948          * 
3949          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3950          * @param {Event} e The event
3951          * @param {Object} data An object containing arbitrary data supplied by the drag source
3952          */
3953          "out" : true,
3954          
3955         /**
3956          * @event drop
3957          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
3958          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
3959          * implementation that does something to process the drop event and returns true so that the drag source's
3960          * repair action does not run.
3961          * 
3962          * IMPORTANT : it should set this.success
3963          * 
3964          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3965          * @param {Event} e The event
3966          * @param {Object} data An object containing arbitrary data supplied by the drag source
3967         */
3968          "drop" : true
3969     });
3970             
3971      
3972     Roo.dd.DropTarget.superclass.constructor.call(  this, 
3973         this.el.dom, 
3974         this.ddGroup || this.group,
3975         {
3976             isTarget: true,
3977             listeners : listeners || {} 
3978            
3979         
3980         }
3981     );
3982
3983 };
3984
3985 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
3986     /**
3987      * @cfg {String} overClass
3988      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
3989      */
3990      /**
3991      * @cfg {String} ddGroup
3992      * The drag drop group to handle drop events for
3993      */
3994      
3995     /**
3996      * @cfg {String} dropAllowed
3997      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3998      */
3999     dropAllowed : "x-dd-drop-ok",
4000     /**
4001      * @cfg {String} dropNotAllowed
4002      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
4003      */
4004     dropNotAllowed : "x-dd-drop-nodrop",
4005     /**
4006      * @cfg {boolean} success
4007      * set this after drop listener.. 
4008      */
4009     success : false,
4010     /**
4011      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
4012      * if the drop point is valid for over/enter..
4013      */
4014     valid : false,
4015     // private
4016     isTarget : true,
4017
4018     // private
4019     isNotifyTarget : true,
4020     
4021     /**
4022      * @hide
4023      */
4024     notifyEnter : function(dd, e, data)
4025     {
4026         this.valid = true;
4027         this.fireEvent('enter', dd, e, data);
4028         if(this.overClass){
4029             this.el.addClass(this.overClass);
4030         }
4031         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4032             this.valid ? this.dropAllowed : this.dropNotAllowed
4033         );
4034     },
4035
4036     /**
4037      * @hide
4038      */
4039     notifyOver : function(dd, e, data)
4040     {
4041         this.valid = true;
4042         this.fireEvent('over', dd, e, data);
4043         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4044             this.valid ? this.dropAllowed : this.dropNotAllowed
4045         );
4046     },
4047
4048     /**
4049      * @hide
4050      */
4051     notifyOut : function(dd, e, data)
4052     {
4053         this.fireEvent('out', dd, e, data);
4054         if(this.overClass){
4055             this.el.removeClass(this.overClass);
4056         }
4057     },
4058
4059     /**
4060      * @hide
4061      */
4062     notifyDrop : function(dd, e, data)
4063     {
4064         this.success = false;
4065         this.fireEvent('drop', dd, e, data);
4066         return this.success;
4067     }
4068 });/*
4069  * Based on:
4070  * Ext JS Library 1.1.1
4071  * Copyright(c) 2006-2007, Ext JS, LLC.
4072  *
4073  * Originally Released Under LGPL - original licence link has changed is not relivant.
4074  *
4075  * Fork - LGPL
4076  * <script type="text/javascript">
4077  */
4078
4079
4080 /**
4081  * @class Roo.dd.DragZone
4082  * @extends Roo.dd.DragSource
4083  * This class provides a container DD instance that proxies for multiple child node sources.<br />
4084  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
4085  * @constructor
4086  * @param {String/HTMLElement/Element} el The container element
4087  * @param {Object} config
4088  */
4089 Roo.dd.DragZone = function(el, config){
4090     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
4091     if(this.containerScroll){
4092         Roo.dd.ScrollManager.register(this.el);
4093     }
4094 };
4095
4096 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
4097     /**
4098      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
4099      * for auto scrolling during drag operations.
4100      */
4101     /**
4102      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
4103      * method after a failed drop (defaults to "c3daf9" - light blue)
4104      */
4105
4106     /**
4107      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
4108      * for a valid target to drag based on the mouse down. Override this method
4109      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
4110      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
4111      * @param {EventObject} e The mouse down event
4112      * @return {Object} The dragData
4113      */
4114     getDragData : function(e){
4115         return Roo.dd.Registry.getHandleFromEvent(e);
4116     },
4117     
4118     /**
4119      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
4120      * this.dragData.ddel
4121      * @param {Number} x The x position of the click on the dragged object
4122      * @param {Number} y The y position of the click on the dragged object
4123      * @return {Boolean} true to continue the drag, false to cancel
4124      */
4125     onInitDrag : function(x, y){
4126         this.proxy.update(this.dragData.ddel.cloneNode(true));
4127         this.onStartDrag(x, y);
4128         return true;
4129     },
4130     
4131     /**
4132      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
4133      */
4134     afterRepair : function(){
4135         if(Roo.enableFx){
4136             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
4137         }
4138         this.dragging = false;
4139     },
4140
4141     /**
4142      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
4143      * the XY of this.dragData.ddel
4144      * @param {EventObject} e The mouse up event
4145      * @return {Array} The xy location (e.g. [100, 200])
4146      */
4147     getRepairXY : function(e){
4148         return Roo.Element.fly(this.dragData.ddel).getXY();  
4149     }
4150 });/*
4151  * Based on:
4152  * Ext JS Library 1.1.1
4153  * Copyright(c) 2006-2007, Ext JS, LLC.
4154  *
4155  * Originally Released Under LGPL - original licence link has changed is not relivant.
4156  *
4157  * Fork - LGPL
4158  * <script type="text/javascript">
4159  */
4160 /**
4161  * @class Roo.dd.DropZone
4162  * @extends Roo.dd.DropTarget
4163  * This class provides a container DD instance that proxies for multiple child node targets.<br />
4164  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
4165  * @constructor
4166  * @param {String/HTMLElement/Element} el The container element
4167  * @param {Object} config
4168  */
4169 Roo.dd.DropZone = function(el, config){
4170     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
4171 };
4172
4173 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
4174     /**
4175      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
4176      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
4177      * provide your own custom lookup.
4178      * @param {Event} e The event
4179      * @return {Object} data The custom data
4180      */
4181     getTargetFromEvent : function(e){
4182         return Roo.dd.Registry.getTargetFromEvent(e);
4183     },
4184
4185     /**
4186      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
4187      * that it has registered.  This method has no default implementation and should be overridden to provide
4188      * node-specific processing if necessary.
4189      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
4190      * {@link #getTargetFromEvent} for this node)
4191      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4192      * @param {Event} e The event
4193      * @param {Object} data An object containing arbitrary data supplied by the drag source
4194      */
4195     onNodeEnter : function(n, dd, e, data){
4196         
4197     },
4198
4199     /**
4200      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
4201      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
4202      * overridden to provide the proper feedback.
4203      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4204      * {@link #getTargetFromEvent} for this node)
4205      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4206      * @param {Event} e The event
4207      * @param {Object} data An object containing arbitrary data supplied by the drag source
4208      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4209      * underlying {@link Roo.dd.StatusProxy} can be updated
4210      */
4211     onNodeOver : function(n, dd, e, data){
4212         return this.dropAllowed;
4213     },
4214
4215     /**
4216      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
4217      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
4218      * node-specific processing if necessary.
4219      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4220      * {@link #getTargetFromEvent} for this node)
4221      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4222      * @param {Event} e The event
4223      * @param {Object} data An object containing arbitrary data supplied by the drag source
4224      */
4225     onNodeOut : function(n, dd, e, data){
4226         
4227     },
4228
4229     /**
4230      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
4231      * the drop node.  The default implementation returns false, so it should be overridden to provide the
4232      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
4233      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4234      * {@link #getTargetFromEvent} for this node)
4235      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4236      * @param {Event} e The event
4237      * @param {Object} data An object containing arbitrary data supplied by the drag source
4238      * @return {Boolean} True if the drop was valid, else false
4239      */
4240     onNodeDrop : function(n, dd, e, data){
4241         return false;
4242     },
4243
4244     /**
4245      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
4246      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
4247      * it should be overridden to provide the proper feedback if necessary.
4248      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4249      * @param {Event} e The event
4250      * @param {Object} data An object containing arbitrary data supplied by the drag source
4251      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4252      * underlying {@link Roo.dd.StatusProxy} can be updated
4253      */
4254     onContainerOver : function(dd, e, data){
4255         return this.dropNotAllowed;
4256     },
4257
4258     /**
4259      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
4260      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
4261      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
4262      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
4263      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4264      * @param {Event} e The event
4265      * @param {Object} data An object containing arbitrary data supplied by the drag source
4266      * @return {Boolean} True if the drop was valid, else false
4267      */
4268     onContainerDrop : function(dd, e, data){
4269         return false;
4270     },
4271
4272     /**
4273      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
4274      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
4275      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
4276      * you should override this method and provide a custom implementation.
4277      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4278      * @param {Event} e The event
4279      * @param {Object} data An object containing arbitrary data supplied by the drag source
4280      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4281      * underlying {@link Roo.dd.StatusProxy} can be updated
4282      */
4283     notifyEnter : function(dd, e, data){
4284         return this.dropNotAllowed;
4285     },
4286
4287     /**
4288      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
4289      * This method will be called on every mouse movement while the drag source is over the drop zone.
4290      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
4291      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
4292      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
4293      * registered node, it will call {@link #onContainerOver}.
4294      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4295      * @param {Event} e The event
4296      * @param {Object} data An object containing arbitrary data supplied by the drag source
4297      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4298      * underlying {@link Roo.dd.StatusProxy} can be updated
4299      */
4300     notifyOver : function(dd, e, data){
4301         var n = this.getTargetFromEvent(e);
4302         if(!n){ // not over valid drop target
4303             if(this.lastOverNode){
4304                 this.onNodeOut(this.lastOverNode, dd, e, data);
4305                 this.lastOverNode = null;
4306             }
4307             return this.onContainerOver(dd, e, data);
4308         }
4309         if(this.lastOverNode != n){
4310             if(this.lastOverNode){
4311                 this.onNodeOut(this.lastOverNode, dd, e, data);
4312             }
4313             this.onNodeEnter(n, dd, e, data);
4314             this.lastOverNode = n;
4315         }
4316         return this.onNodeOver(n, dd, e, data);
4317     },
4318
4319     /**
4320      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
4321      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
4322      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
4323      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
4324      * @param {Event} e The event
4325      * @param {Object} data An object containing arbitrary data supplied by the drag zone
4326      */
4327     notifyOut : function(dd, e, data){
4328         if(this.lastOverNode){
4329             this.onNodeOut(this.lastOverNode, dd, e, data);
4330             this.lastOverNode = null;
4331         }
4332     },
4333
4334     /**
4335      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
4336      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
4337      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
4338      * otherwise it will call {@link #onContainerDrop}.
4339      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4340      * @param {Event} e The event
4341      * @param {Object} data An object containing arbitrary data supplied by the drag source
4342      * @return {Boolean} True if the drop was valid, else false
4343      */
4344     notifyDrop : function(dd, e, data){
4345         if(this.lastOverNode){
4346             this.onNodeOut(this.lastOverNode, dd, e, data);
4347             this.lastOverNode = null;
4348         }
4349         var n = this.getTargetFromEvent(e);
4350         return n ?
4351             this.onNodeDrop(n, dd, e, data) :
4352             this.onContainerDrop(dd, e, data);
4353     },
4354
4355     // private
4356     triggerCacheRefresh : function(){
4357         Roo.dd.DDM.refreshCache(this.groups);
4358     }  
4359 });/*
4360  * Based on:
4361  * Ext JS Library 1.1.1
4362  * Copyright(c) 2006-2007, Ext JS, LLC.
4363  *
4364  * Originally Released Under LGPL - original licence link has changed is not relivant.
4365  *
4366  * Fork - LGPL
4367  * <script type="text/javascript">
4368  */
4369
4370
4371 /**
4372  * @class Roo.data.SortTypes
4373  * @singleton
4374  * Defines the default sorting (casting?) comparison functions used when sorting data.
4375  */
4376 Roo.data.SortTypes = {
4377     /**
4378      * Default sort that does nothing
4379      * @param {Mixed} s The value being converted
4380      * @return {Mixed} The comparison value
4381      */
4382     none : function(s){
4383         return s;
4384     },
4385     
4386     /**
4387      * The regular expression used to strip tags
4388      * @type {RegExp}
4389      * @property
4390      */
4391     stripTagsRE : /<\/?[^>]+>/gi,
4392     
4393     /**
4394      * Strips all HTML tags to sort on text only
4395      * @param {Mixed} s The value being converted
4396      * @return {String} The comparison value
4397      */
4398     asText : function(s){
4399         return String(s).replace(this.stripTagsRE, "");
4400     },
4401     
4402     /**
4403      * Strips all HTML tags to sort on text only - Case insensitive
4404      * @param {Mixed} s The value being converted
4405      * @return {String} The comparison value
4406      */
4407     asUCText : function(s){
4408         return String(s).toUpperCase().replace(this.stripTagsRE, "");
4409     },
4410     
4411     /**
4412      * Case insensitive string
4413      * @param {Mixed} s The value being converted
4414      * @return {String} The comparison value
4415      */
4416     asUCString : function(s) {
4417         return String(s).toUpperCase();
4418     },
4419     
4420     /**
4421      * Date sorting
4422      * @param {Mixed} s The value being converted
4423      * @return {Number} The comparison value
4424      */
4425     asDate : function(s) {
4426         if(!s){
4427             return 0;
4428         }
4429         if(s instanceof Date){
4430             return s.getTime();
4431         }
4432         return Date.parse(String(s));
4433     },
4434     
4435     /**
4436      * Float sorting
4437      * @param {Mixed} s The value being converted
4438      * @return {Float} The comparison value
4439      */
4440     asFloat : function(s) {
4441         var val = parseFloat(String(s).replace(/,/g, ""));
4442         if(isNaN(val)) val = 0;
4443         return val;
4444     },
4445     
4446     /**
4447      * Integer sorting
4448      * @param {Mixed} s The value being converted
4449      * @return {Number} The comparison value
4450      */
4451     asInt : function(s) {
4452         var val = parseInt(String(s).replace(/,/g, ""));
4453         if(isNaN(val)) val = 0;
4454         return val;
4455     }
4456 };/*
4457  * Based on:
4458  * Ext JS Library 1.1.1
4459  * Copyright(c) 2006-2007, Ext JS, LLC.
4460  *
4461  * Originally Released Under LGPL - original licence link has changed is not relivant.
4462  *
4463  * Fork - LGPL
4464  * <script type="text/javascript">
4465  */
4466
4467 /**
4468 * @class Roo.data.Record
4469  * Instances of this class encapsulate both record <em>definition</em> information, and record
4470  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
4471  * to access Records cached in an {@link Roo.data.Store} object.<br>
4472  * <p>
4473  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
4474  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
4475  * objects.<br>
4476  * <p>
4477  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
4478  * @constructor
4479  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
4480  * {@link #create}. The parameters are the same.
4481  * @param {Array} data An associative Array of data values keyed by the field name.
4482  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
4483  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
4484  * not specified an integer id is generated.
4485  */
4486 Roo.data.Record = function(data, id){
4487     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
4488     this.data = data;
4489 };
4490
4491 /**
4492  * Generate a constructor for a specific record layout.
4493  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
4494  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
4495  * Each field definition object may contain the following properties: <ul>
4496  * <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,
4497  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
4498  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
4499  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
4500  * is being used, then this is a string containing the javascript expression to reference the data relative to 
4501  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
4502  * to the data item relative to the record element. If the mapping expression is the same as the field name,
4503  * this may be omitted.</p></li>
4504  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
4505  * <ul><li>auto (Default, implies no conversion)</li>
4506  * <li>string</li>
4507  * <li>int</li>
4508  * <li>float</li>
4509  * <li>boolean</li>
4510  * <li>date</li></ul></p></li>
4511  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
4512  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
4513  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
4514  * by the Reader into an object that will be stored in the Record. It is passed the
4515  * following parameters:<ul>
4516  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
4517  * </ul></p></li>
4518  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
4519  * </ul>
4520  * <br>usage:<br><pre><code>
4521 var TopicRecord = Roo.data.Record.create(
4522     {name: 'title', mapping: 'topic_title'},
4523     {name: 'author', mapping: 'username'},
4524     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
4525     {name: 'lastPost', mapping: 'post_time', type: 'date'},
4526     {name: 'lastPoster', mapping: 'user2'},
4527     {name: 'excerpt', mapping: 'post_text'}
4528 );
4529
4530 var myNewRecord = new TopicRecord({
4531     title: 'Do my job please',
4532     author: 'noobie',
4533     totalPosts: 1,
4534     lastPost: new Date(),
4535     lastPoster: 'Animal',
4536     excerpt: 'No way dude!'
4537 });
4538 myStore.add(myNewRecord);
4539 </code></pre>
4540  * @method create
4541  * @static
4542  */
4543 Roo.data.Record.create = function(o){
4544     var f = function(){
4545         f.superclass.constructor.apply(this, arguments);
4546     };
4547     Roo.extend(f, Roo.data.Record);
4548     var p = f.prototype;
4549     p.fields = new Roo.util.MixedCollection(false, function(field){
4550         return field.name;
4551     });
4552     for(var i = 0, len = o.length; i < len; i++){
4553         p.fields.add(new Roo.data.Field(o[i]));
4554     }
4555     f.getField = function(name){
4556         return p.fields.get(name);  
4557     };
4558     return f;
4559 };
4560
4561 Roo.data.Record.AUTO_ID = 1000;
4562 Roo.data.Record.EDIT = 'edit';
4563 Roo.data.Record.REJECT = 'reject';
4564 Roo.data.Record.COMMIT = 'commit';
4565
4566 Roo.data.Record.prototype = {
4567     /**
4568      * Readonly flag - true if this record has been modified.
4569      * @type Boolean
4570      */
4571     dirty : false,
4572     editing : false,
4573     error: null,
4574     modified: null,
4575
4576     // private
4577     join : function(store){
4578         this.store = store;
4579     },
4580
4581     /**
4582      * Set the named field to the specified value.
4583      * @param {String} name The name of the field to set.
4584      * @param {Object} value The value to set the field to.
4585      */
4586     set : function(name, value){
4587         if(this.data[name] == value){
4588             return;
4589         }
4590         this.dirty = true;
4591         if(!this.modified){
4592             this.modified = {};
4593         }
4594         if(typeof this.modified[name] == 'undefined'){
4595             this.modified[name] = this.data[name];
4596         }
4597         this.data[name] = value;
4598         if(!this.editing && this.store){
4599             this.store.afterEdit(this);
4600         }       
4601     },
4602
4603     /**
4604      * Get the value of the named field.
4605      * @param {String} name The name of the field to get the value of.
4606      * @return {Object} The value of the field.
4607      */
4608     get : function(name){
4609         return this.data[name]; 
4610     },
4611
4612     // private
4613     beginEdit : function(){
4614         this.editing = true;
4615         this.modified = {}; 
4616     },
4617
4618     // private
4619     cancelEdit : function(){
4620         this.editing = false;
4621         delete this.modified;
4622     },
4623
4624     // private
4625     endEdit : function(){
4626         this.editing = false;
4627         if(this.dirty && this.store){
4628             this.store.afterEdit(this);
4629         }
4630     },
4631
4632     /**
4633      * Usually called by the {@link Roo.data.Store} which owns the Record.
4634      * Rejects all changes made to the Record since either creation, or the last commit operation.
4635      * Modified fields are reverted to their original values.
4636      * <p>
4637      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4638      * of reject operations.
4639      */
4640     reject : function(){
4641         var m = this.modified;
4642         for(var n in m){
4643             if(typeof m[n] != "function"){
4644                 this.data[n] = m[n];
4645             }
4646         }
4647         this.dirty = false;
4648         delete this.modified;
4649         this.editing = false;
4650         if(this.store){
4651             this.store.afterReject(this);
4652         }
4653     },
4654
4655     /**
4656      * Usually called by the {@link Roo.data.Store} which owns the Record.
4657      * Commits all changes made to the Record since either creation, or the last commit operation.
4658      * <p>
4659      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4660      * of commit operations.
4661      */
4662     commit : function(){
4663         this.dirty = false;
4664         delete this.modified;
4665         this.editing = false;
4666         if(this.store){
4667             this.store.afterCommit(this);
4668         }
4669     },
4670
4671     // private
4672     hasError : function(){
4673         return this.error != null;
4674     },
4675
4676     // private
4677     clearError : function(){
4678         this.error = null;
4679     },
4680
4681     /**
4682      * Creates a copy of this record.
4683      * @param {String} id (optional) A new record id if you don't want to use this record's id
4684      * @return {Record}
4685      */
4686     copy : function(newId) {
4687         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
4688     }
4689 };/*
4690  * Based on:
4691  * Ext JS Library 1.1.1
4692  * Copyright(c) 2006-2007, Ext JS, LLC.
4693  *
4694  * Originally Released Under LGPL - original licence link has changed is not relivant.
4695  *
4696  * Fork - LGPL
4697  * <script type="text/javascript">
4698  */
4699
4700
4701
4702 /**
4703  * @class Roo.data.Store
4704  * @extends Roo.util.Observable
4705  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
4706  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
4707  * <p>
4708  * 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
4709  * has no knowledge of the format of the data returned by the Proxy.<br>
4710  * <p>
4711  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
4712  * instances from the data object. These records are cached and made available through accessor functions.
4713  * @constructor
4714  * Creates a new Store.
4715  * @param {Object} config A config object containing the objects needed for the Store to access data,
4716  * and read the data into Records.
4717  */
4718 Roo.data.Store = function(config){
4719     this.data = new Roo.util.MixedCollection(false);
4720     this.data.getKey = function(o){
4721         return o.id;
4722     };
4723     this.baseParams = {};
4724     // private
4725     this.paramNames = {
4726         "start" : "start",
4727         "limit" : "limit",
4728         "sort" : "sort",
4729         "dir" : "dir",
4730         "multisort" : "_multisort"
4731     };
4732
4733     if(config && config.data){
4734         this.inlineData = config.data;
4735         delete config.data;
4736     }
4737
4738     Roo.apply(this, config);
4739     
4740     if(this.reader){ // reader passed
4741         this.reader = Roo.factory(this.reader, Roo.data);
4742         this.reader.xmodule = this.xmodule || false;
4743         if(!this.recordType){
4744             this.recordType = this.reader.recordType;
4745         }
4746         if(this.reader.onMetaChange){
4747             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
4748         }
4749     }
4750
4751     if(this.recordType){
4752         this.fields = this.recordType.prototype.fields;
4753     }
4754     this.modified = [];
4755
4756     this.addEvents({
4757         /**
4758          * @event datachanged
4759          * Fires when the data cache has changed, and a widget which is using this Store
4760          * as a Record cache should refresh its view.
4761          * @param {Store} this
4762          */
4763         datachanged : true,
4764         /**
4765          * @event metachange
4766          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
4767          * @param {Store} this
4768          * @param {Object} meta The JSON metadata
4769          */
4770         metachange : true,
4771         /**
4772          * @event add
4773          * Fires when Records have been added to the Store
4774          * @param {Store} this
4775          * @param {Roo.data.Record[]} records The array of Records added
4776          * @param {Number} index The index at which the record(s) were added
4777          */
4778         add : true,
4779         /**
4780          * @event remove
4781          * Fires when a Record has been removed from the Store
4782          * @param {Store} this
4783          * @param {Roo.data.Record} record The Record that was removed
4784          * @param {Number} index The index at which the record was removed
4785          */
4786         remove : true,
4787         /**
4788          * @event update
4789          * Fires when a Record has been updated
4790          * @param {Store} this
4791          * @param {Roo.data.Record} record The Record that was updated
4792          * @param {String} operation The update operation being performed.  Value may be one of:
4793          * <pre><code>
4794  Roo.data.Record.EDIT
4795  Roo.data.Record.REJECT
4796  Roo.data.Record.COMMIT
4797          * </code></pre>
4798          */
4799         update : true,
4800         /**
4801          * @event clear
4802          * Fires when the data cache has been cleared.
4803          * @param {Store} this
4804          */
4805         clear : true,
4806         /**
4807          * @event beforeload
4808          * Fires before a request is made for a new data object.  If the beforeload handler returns false
4809          * the load action will be canceled.
4810          * @param {Store} this
4811          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4812          */
4813         beforeload : true,
4814         /**
4815          * @event beforeloadadd
4816          * Fires after a new set of Records has been loaded.
4817          * @param {Store} this
4818          * @param {Roo.data.Record[]} records The Records that were loaded
4819          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4820          */
4821         beforeloadadd : true,
4822         /**
4823          * @event load
4824          * Fires after a new set of Records has been loaded, before they are added to the store.
4825          * @param {Store} this
4826          * @param {Roo.data.Record[]} records The Records that were loaded
4827          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4828          * @params {Object} return from reader
4829          */
4830         load : true,
4831         /**
4832          * @event loadexception
4833          * Fires if an exception occurs in the Proxy during loading.
4834          * Called with the signature of the Proxy's "loadexception" event.
4835          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
4836          * 
4837          * @param {Proxy} 
4838          * @param {Object} return from JsonData.reader() - success, totalRecords, records
4839          * @param {Object} load options 
4840          * @param {Object} jsonData from your request (normally this contains the Exception)
4841          */
4842         loadexception : true
4843     });
4844     
4845     if(this.proxy){
4846         this.proxy = Roo.factory(this.proxy, Roo.data);
4847         this.proxy.xmodule = this.xmodule || false;
4848         this.relayEvents(this.proxy,  ["loadexception"]);
4849     }
4850     this.sortToggle = {};
4851     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
4852
4853     Roo.data.Store.superclass.constructor.call(this);
4854
4855     if(this.inlineData){
4856         this.loadData(this.inlineData);
4857         delete this.inlineData;
4858     }
4859 };
4860
4861 Roo.extend(Roo.data.Store, Roo.util.Observable, {
4862      /**
4863     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
4864     * without a remote query - used by combo/forms at present.
4865     */
4866     
4867     /**
4868     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
4869     */
4870     /**
4871     * @cfg {Array} data Inline data to be loaded when the store is initialized.
4872     */
4873     /**
4874     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
4875     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
4876     */
4877     /**
4878     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
4879     * on any HTTP request
4880     */
4881     /**
4882     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
4883     */
4884     /**
4885     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
4886     */
4887     multiSort: false,
4888     /**
4889     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
4890     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
4891     */
4892     remoteSort : false,
4893
4894     /**
4895     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
4896      * loaded or when a record is removed. (defaults to false).
4897     */
4898     pruneModifiedRecords : false,
4899
4900     // private
4901     lastOptions : null,
4902
4903     /**
4904      * Add Records to the Store and fires the add event.
4905      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4906      */
4907     add : function(records){
4908         records = [].concat(records);
4909         for(var i = 0, len = records.length; i < len; i++){
4910             records[i].join(this);
4911         }
4912         var index = this.data.length;
4913         this.data.addAll(records);
4914         this.fireEvent("add", this, records, index);
4915     },
4916
4917     /**
4918      * Remove a Record from the Store and fires the remove event.
4919      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
4920      */
4921     remove : function(record){
4922         var index = this.data.indexOf(record);
4923         this.data.removeAt(index);
4924         if(this.pruneModifiedRecords){
4925             this.modified.remove(record);
4926         }
4927         this.fireEvent("remove", this, record, index);
4928     },
4929
4930     /**
4931      * Remove all Records from the Store and fires the clear event.
4932      */
4933     removeAll : function(){
4934         this.data.clear();
4935         if(this.pruneModifiedRecords){
4936             this.modified = [];
4937         }
4938         this.fireEvent("clear", this);
4939     },
4940
4941     /**
4942      * Inserts Records to the Store at the given index and fires the add event.
4943      * @param {Number} index The start index at which to insert the passed Records.
4944      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4945      */
4946     insert : function(index, records){
4947         records = [].concat(records);
4948         for(var i = 0, len = records.length; i < len; i++){
4949             this.data.insert(index, records[i]);
4950             records[i].join(this);
4951         }
4952         this.fireEvent("add", this, records, index);
4953     },
4954
4955     /**
4956      * Get the index within the cache of the passed Record.
4957      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
4958      * @return {Number} The index of the passed Record. Returns -1 if not found.
4959      */
4960     indexOf : function(record){
4961         return this.data.indexOf(record);
4962     },
4963
4964     /**
4965      * Get the index within the cache of the Record with the passed id.
4966      * @param {String} id The id of the Record to find.
4967      * @return {Number} The index of the Record. Returns -1 if not found.
4968      */
4969     indexOfId : function(id){
4970         return this.data.indexOfKey(id);
4971     },
4972
4973     /**
4974      * Get the Record with the specified id.
4975      * @param {String} id The id of the Record to find.
4976      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
4977      */
4978     getById : function(id){
4979         return this.data.key(id);
4980     },
4981
4982     /**
4983      * Get the Record at the specified index.
4984      * @param {Number} index The index of the Record to find.
4985      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
4986      */
4987     getAt : function(index){
4988         return this.data.itemAt(index);
4989     },
4990
4991     /**
4992      * Returns a range of Records between specified indices.
4993      * @param {Number} startIndex (optional) The starting index (defaults to 0)
4994      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
4995      * @return {Roo.data.Record[]} An array of Records
4996      */
4997     getRange : function(start, end){
4998         return this.data.getRange(start, end);
4999     },
5000
5001     // private
5002     storeOptions : function(o){
5003         o = Roo.apply({}, o);
5004         delete o.callback;
5005         delete o.scope;
5006         this.lastOptions = o;
5007     },
5008
5009     /**
5010      * Loads the Record cache from the configured Proxy using the configured Reader.
5011      * <p>
5012      * If using remote paging, then the first load call must specify the <em>start</em>
5013      * and <em>limit</em> properties in the options.params property to establish the initial
5014      * position within the dataset, and the number of Records to cache on each read from the Proxy.
5015      * <p>
5016      * <strong>It is important to note that for remote data sources, loading is asynchronous,
5017      * and this call will return before the new data has been loaded. Perform any post-processing
5018      * in a callback function, or in a "load" event handler.</strong>
5019      * <p>
5020      * @param {Object} options An object containing properties which control loading options:<ul>
5021      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
5022      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
5023      * passed the following arguments:<ul>
5024      * <li>r : Roo.data.Record[]</li>
5025      * <li>options: Options object from the load call</li>
5026      * <li>success: Boolean success indicator</li></ul></li>
5027      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
5028      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
5029      * </ul>
5030      */
5031     load : function(options){
5032         options = options || {};
5033         if(this.fireEvent("beforeload", this, options) !== false){
5034             this.storeOptions(options);
5035             var p = Roo.apply(options.params || {}, this.baseParams);
5036             // if meta was not loaded from remote source.. try requesting it.
5037             if (!this.reader.metaFromRemote) {
5038                 p._requestMeta = 1;
5039             }
5040             if(this.sortInfo && this.remoteSort){
5041                 var pn = this.paramNames;
5042                 p[pn["sort"]] = this.sortInfo.field;
5043                 p[pn["dir"]] = this.sortInfo.direction;
5044             }
5045             if (this.multiSort) {
5046                 var pn = this.paramNames;
5047                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
5048             }
5049             
5050             this.proxy.load(p, this.reader, this.loadRecords, this, options);
5051         }
5052     },
5053
5054     /**
5055      * Reloads the Record cache from the configured Proxy using the configured Reader and
5056      * the options from the last load operation performed.
5057      * @param {Object} options (optional) An object containing properties which may override the options
5058      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
5059      * the most recently used options are reused).
5060      */
5061     reload : function(options){
5062         this.load(Roo.applyIf(options||{}, this.lastOptions));
5063     },
5064
5065     // private
5066     // Called as a callback by the Reader during a load operation.
5067     loadRecords : function(o, options, success){
5068         if(!o || success === false){
5069             if(success !== false){
5070                 this.fireEvent("load", this, [], options, o);
5071             }
5072             if(options.callback){
5073                 options.callback.call(options.scope || this, [], options, false);
5074             }
5075             return;
5076         }
5077         // if data returned failure - throw an exception.
5078         if (o.success === false) {
5079             // show a message if no listener is registered.
5080             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
5081                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
5082             }
5083             // loadmask wil be hooked into this..
5084             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
5085             return;
5086         }
5087         var r = o.records, t = o.totalRecords || r.length;
5088         
5089         this.fireEvent("beforeloadadd", this, r, options, o);
5090         
5091         if(!options || options.add !== true){
5092             if(this.pruneModifiedRecords){
5093                 this.modified = [];
5094             }
5095             for(var i = 0, len = r.length; i < len; i++){
5096                 r[i].join(this);
5097             }
5098             if(this.snapshot){
5099                 this.data = this.snapshot;
5100                 delete this.snapshot;
5101             }
5102             this.data.clear();
5103             this.data.addAll(r);
5104             this.totalLength = t;
5105             this.applySort();
5106             this.fireEvent("datachanged", this);
5107         }else{
5108             this.totalLength = Math.max(t, this.data.length+r.length);
5109             this.add(r);
5110         }
5111         this.fireEvent("load", this, r, options, o);
5112         if(options.callback){
5113             options.callback.call(options.scope || this, r, options, true);
5114         }
5115     },
5116
5117
5118     /**
5119      * Loads data from a passed data block. A Reader which understands the format of the data
5120      * must have been configured in the constructor.
5121      * @param {Object} data The data block from which to read the Records.  The format of the data expected
5122      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
5123      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
5124      */
5125     loadData : function(o, append){
5126         var r = this.reader.readRecords(o);
5127         this.loadRecords(r, {add: append}, true);
5128     },
5129
5130     /**
5131      * Gets the number of cached records.
5132      * <p>
5133      * <em>If using paging, this may not be the total size of the dataset. If the data object
5134      * used by the Reader contains the dataset size, then the getTotalCount() function returns
5135      * the data set size</em>
5136      */
5137     getCount : function(){
5138         return this.data.length || 0;
5139     },
5140
5141     /**
5142      * Gets the total number of records in the dataset as returned by the server.
5143      * <p>
5144      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
5145      * the dataset size</em>
5146      */
5147     getTotalCount : function(){
5148         return this.totalLength || 0;
5149     },
5150
5151     /**
5152      * Returns the sort state of the Store as an object with two properties:
5153      * <pre><code>
5154  field {String} The name of the field by which the Records are sorted
5155  direction {String} The sort order, "ASC" or "DESC"
5156      * </code></pre>
5157      */
5158     getSortState : function(){
5159         return this.sortInfo;
5160     },
5161
5162     // private
5163     applySort : function(){
5164         if(this.sortInfo && !this.remoteSort){
5165             var s = this.sortInfo, f = s.field;
5166             var st = this.fields.get(f).sortType;
5167             var fn = function(r1, r2){
5168                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
5169                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5170             };
5171             this.data.sort(s.direction, fn);
5172             if(this.snapshot && this.snapshot != this.data){
5173                 this.snapshot.sort(s.direction, fn);
5174             }
5175         }
5176     },
5177
5178     /**
5179      * Sets the default sort column and order to be used by the next load operation.
5180      * @param {String} fieldName The name of the field to sort by.
5181      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5182      */
5183     setDefaultSort : function(field, dir){
5184         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
5185     },
5186
5187     /**
5188      * Sort the Records.
5189      * If remote sorting is used, the sort is performed on the server, and the cache is
5190      * reloaded. If local sorting is used, the cache is sorted internally.
5191      * @param {String} fieldName The name of the field to sort by.
5192      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5193      */
5194     sort : function(fieldName, dir){
5195         var f = this.fields.get(fieldName);
5196         if(!dir){
5197             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
5198             
5199             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
5200                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
5201             }else{
5202                 dir = f.sortDir;
5203             }
5204         }
5205         this.sortToggle[f.name] = dir;
5206         this.sortInfo = {field: f.name, direction: dir};
5207         if(!this.remoteSort){
5208             this.applySort();
5209             this.fireEvent("datachanged", this);
5210         }else{
5211             this.load(this.lastOptions);
5212         }
5213     },
5214
5215     /**
5216      * Calls the specified function for each of the Records in the cache.
5217      * @param {Function} fn The function to call. The Record is passed as the first parameter.
5218      * Returning <em>false</em> aborts and exits the iteration.
5219      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
5220      */
5221     each : function(fn, scope){
5222         this.data.each(fn, scope);
5223     },
5224
5225     /**
5226      * Gets all records modified since the last commit.  Modified records are persisted across load operations
5227      * (e.g., during paging).
5228      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
5229      */
5230     getModifiedRecords : function(){
5231         return this.modified;
5232     },
5233
5234     // private
5235     createFilterFn : function(property, value, anyMatch){
5236         if(!value.exec){ // not a regex
5237             value = String(value);
5238             if(value.length == 0){
5239                 return false;
5240             }
5241             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
5242         }
5243         return function(r){
5244             return value.test(r.data[property]);
5245         };
5246     },
5247
5248     /**
5249      * Sums the value of <i>property</i> for each record between start and end and returns the result.
5250      * @param {String} property A field on your records
5251      * @param {Number} start The record index to start at (defaults to 0)
5252      * @param {Number} end The last record index to include (defaults to length - 1)
5253      * @return {Number} The sum
5254      */
5255     sum : function(property, start, end){
5256         var rs = this.data.items, v = 0;
5257         start = start || 0;
5258         end = (end || end === 0) ? end : rs.length-1;
5259
5260         for(var i = start; i <= end; i++){
5261             v += (rs[i].data[property] || 0);
5262         }
5263         return v;
5264     },
5265
5266     /**
5267      * Filter the records by a specified property.
5268      * @param {String} field A field on your records
5269      * @param {String/RegExp} value Either a string that the field
5270      * should start with or a RegExp to test against the field
5271      * @param {Boolean} anyMatch True to match any part not just the beginning
5272      */
5273     filter : function(property, value, anyMatch){
5274         var fn = this.createFilterFn(property, value, anyMatch);
5275         return fn ? this.filterBy(fn) : this.clearFilter();
5276     },
5277
5278     /**
5279      * Filter by a function. The specified function will be called with each
5280      * record in this data source. If the function returns true the record is included,
5281      * otherwise it is filtered.
5282      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5283      * @param {Object} scope (optional) The scope of the function (defaults to this)
5284      */
5285     filterBy : function(fn, scope){
5286         this.snapshot = this.snapshot || this.data;
5287         this.data = this.queryBy(fn, scope||this);
5288         this.fireEvent("datachanged", this);
5289     },
5290
5291     /**
5292      * Query the records by a specified property.
5293      * @param {String} field A field on your records
5294      * @param {String/RegExp} value Either a string that the field
5295      * should start with or a RegExp to test against the field
5296      * @param {Boolean} anyMatch True to match any part not just the beginning
5297      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5298      */
5299     query : function(property, value, anyMatch){
5300         var fn = this.createFilterFn(property, value, anyMatch);
5301         return fn ? this.queryBy(fn) : this.data.clone();
5302     },
5303
5304     /**
5305      * Query by a function. The specified function will be called with each
5306      * record in this data source. If the function returns true the record is included
5307      * in the results.
5308      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5309      * @param {Object} scope (optional) The scope of the function (defaults to this)
5310       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5311      **/
5312     queryBy : function(fn, scope){
5313         var data = this.snapshot || this.data;
5314         return data.filterBy(fn, scope||this);
5315     },
5316
5317     /**
5318      * Collects unique values for a particular dataIndex from this store.
5319      * @param {String} dataIndex The property to collect
5320      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
5321      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
5322      * @return {Array} An array of the unique values
5323      **/
5324     collect : function(dataIndex, allowNull, bypassFilter){
5325         var d = (bypassFilter === true && this.snapshot) ?
5326                 this.snapshot.items : this.data.items;
5327         var v, sv, r = [], l = {};
5328         for(var i = 0, len = d.length; i < len; i++){
5329             v = d[i].data[dataIndex];
5330             sv = String(v);
5331             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
5332                 l[sv] = true;
5333                 r[r.length] = v;
5334             }
5335         }
5336         return r;
5337     },
5338
5339     /**
5340      * Revert to a view of the Record cache with no filtering applied.
5341      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
5342      */
5343     clearFilter : function(suppressEvent){
5344         if(this.snapshot && this.snapshot != this.data){
5345             this.data = this.snapshot;
5346             delete this.snapshot;
5347             if(suppressEvent !== true){
5348                 this.fireEvent("datachanged", this);
5349             }
5350         }
5351     },
5352
5353     // private
5354     afterEdit : function(record){
5355         if(this.modified.indexOf(record) == -1){
5356             this.modified.push(record);
5357         }
5358         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
5359     },
5360     
5361     // private
5362     afterReject : function(record){
5363         this.modified.remove(record);
5364         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
5365     },
5366
5367     // private
5368     afterCommit : function(record){
5369         this.modified.remove(record);
5370         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
5371     },
5372
5373     /**
5374      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
5375      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
5376      */
5377     commitChanges : function(){
5378         var m = this.modified.slice(0);
5379         this.modified = [];
5380         for(var i = 0, len = m.length; i < len; i++){
5381             m[i].commit();
5382         }
5383     },
5384
5385     /**
5386      * Cancel outstanding changes on all changed records.
5387      */
5388     rejectChanges : function(){
5389         var m = this.modified.slice(0);
5390         this.modified = [];
5391         for(var i = 0, len = m.length; i < len; i++){
5392             m[i].reject();
5393         }
5394     },
5395
5396     onMetaChange : function(meta, rtype, o){
5397         this.recordType = rtype;
5398         this.fields = rtype.prototype.fields;
5399         delete this.snapshot;
5400         this.sortInfo = meta.sortInfo || this.sortInfo;
5401         this.modified = [];
5402         this.fireEvent('metachange', this, this.reader.meta);
5403     }
5404 });/*
5405  * Based on:
5406  * Ext JS Library 1.1.1
5407  * Copyright(c) 2006-2007, Ext JS, LLC.
5408  *
5409  * Originally Released Under LGPL - original licence link has changed is not relivant.
5410  *
5411  * Fork - LGPL
5412  * <script type="text/javascript">
5413  */
5414
5415 /**
5416  * @class Roo.data.SimpleStore
5417  * @extends Roo.data.Store
5418  * Small helper class to make creating Stores from Array data easier.
5419  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
5420  * @cfg {Array} fields An array of field definition objects, or field name strings.
5421  * @cfg {Array} data The multi-dimensional array of data
5422  * @constructor
5423  * @param {Object} config
5424  */
5425 Roo.data.SimpleStore = function(config){
5426     Roo.data.SimpleStore.superclass.constructor.call(this, {
5427         isLocal : true,
5428         reader: new Roo.data.ArrayReader({
5429                 id: config.id
5430             },
5431             Roo.data.Record.create(config.fields)
5432         ),
5433         proxy : new Roo.data.MemoryProxy(config.data)
5434     });
5435     this.load();
5436 };
5437 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
5438  * Based on:
5439  * Ext JS Library 1.1.1
5440  * Copyright(c) 2006-2007, Ext JS, LLC.
5441  *
5442  * Originally Released Under LGPL - original licence link has changed is not relivant.
5443  *
5444  * Fork - LGPL
5445  * <script type="text/javascript">
5446  */
5447
5448 /**
5449 /**
5450  * @extends Roo.data.Store
5451  * @class Roo.data.JsonStore
5452  * Small helper class to make creating Stores for JSON data easier. <br/>
5453 <pre><code>
5454 var store = new Roo.data.JsonStore({
5455     url: 'get-images.php',
5456     root: 'images',
5457     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
5458 });
5459 </code></pre>
5460  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
5461  * JsonReader and HttpProxy (unless inline data is provided).</b>
5462  * @cfg {Array} fields An array of field definition objects, or field name strings.
5463  * @constructor
5464  * @param {Object} config
5465  */
5466 Roo.data.JsonStore = function(c){
5467     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
5468         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
5469         reader: new Roo.data.JsonReader(c, c.fields)
5470     }));
5471 };
5472 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
5473  * Based on:
5474  * Ext JS Library 1.1.1
5475  * Copyright(c) 2006-2007, Ext JS, LLC.
5476  *
5477  * Originally Released Under LGPL - original licence link has changed is not relivant.
5478  *
5479  * Fork - LGPL
5480  * <script type="text/javascript">
5481  */
5482
5483  
5484 Roo.data.Field = function(config){
5485     if(typeof config == "string"){
5486         config = {name: config};
5487     }
5488     Roo.apply(this, config);
5489     
5490     if(!this.type){
5491         this.type = "auto";
5492     }
5493     
5494     var st = Roo.data.SortTypes;
5495     // named sortTypes are supported, here we look them up
5496     if(typeof this.sortType == "string"){
5497         this.sortType = st[this.sortType];
5498     }
5499     
5500     // set default sortType for strings and dates
5501     if(!this.sortType){
5502         switch(this.type){
5503             case "string":
5504                 this.sortType = st.asUCString;
5505                 break;
5506             case "date":
5507                 this.sortType = st.asDate;
5508                 break;
5509             default:
5510                 this.sortType = st.none;
5511         }
5512     }
5513
5514     // define once
5515     var stripRe = /[\$,%]/g;
5516
5517     // prebuilt conversion function for this field, instead of
5518     // switching every time we're reading a value
5519     if(!this.convert){
5520         var cv, dateFormat = this.dateFormat;
5521         switch(this.type){
5522             case "":
5523             case "auto":
5524             case undefined:
5525                 cv = function(v){ return v; };
5526                 break;
5527             case "string":
5528                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
5529                 break;
5530             case "int":
5531                 cv = function(v){
5532                     return v !== undefined && v !== null && v !== '' ?
5533                            parseInt(String(v).replace(stripRe, ""), 10) : '';
5534                     };
5535                 break;
5536             case "float":
5537                 cv = function(v){
5538                     return v !== undefined && v !== null && v !== '' ?
5539                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
5540                     };
5541                 break;
5542             case "bool":
5543             case "boolean":
5544                 cv = function(v){ return v === true || v === "true" || v == 1; };
5545                 break;
5546             case "date":
5547                 cv = function(v){
5548                     if(!v){
5549                         return '';
5550                     }
5551                     if(v instanceof Date){
5552                         return v;
5553                     }
5554                     if(dateFormat){
5555                         if(dateFormat == "timestamp"){
5556                             return new Date(v*1000);
5557                         }
5558                         return Date.parseDate(v, dateFormat);
5559                     }
5560                     var parsed = Date.parse(v);
5561                     return parsed ? new Date(parsed) : null;
5562                 };
5563              break;
5564             
5565         }
5566         this.convert = cv;
5567     }
5568 };
5569
5570 Roo.data.Field.prototype = {
5571     dateFormat: null,
5572     defaultValue: "",
5573     mapping: null,
5574     sortType : null,
5575     sortDir : "ASC"
5576 };/*
5577  * Based on:
5578  * Ext JS Library 1.1.1
5579  * Copyright(c) 2006-2007, Ext JS, LLC.
5580  *
5581  * Originally Released Under LGPL - original licence link has changed is not relivant.
5582  *
5583  * Fork - LGPL
5584  * <script type="text/javascript">
5585  */
5586  
5587 // Base class for reading structured data from a data source.  This class is intended to be
5588 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
5589
5590 /**
5591  * @class Roo.data.DataReader
5592  * Base class for reading structured data from a data source.  This class is intended to be
5593  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
5594  */
5595
5596 Roo.data.DataReader = function(meta, recordType){
5597     
5598     this.meta = meta;
5599     
5600     this.recordType = recordType instanceof Array ? 
5601         Roo.data.Record.create(recordType) : recordType;
5602 };
5603
5604 Roo.data.DataReader.prototype = {
5605      /**
5606      * Create an empty record
5607      * @param {Object} data (optional) - overlay some values
5608      * @return {Roo.data.Record} record created.
5609      */
5610     newRow :  function(d) {
5611         var da =  {};
5612         this.recordType.prototype.fields.each(function(c) {
5613             switch( c.type) {
5614                 case 'int' : da[c.name] = 0; break;
5615                 case 'date' : da[c.name] = new Date(); break;
5616                 case 'float' : da[c.name] = 0.0; break;
5617                 case 'boolean' : da[c.name] = false; break;
5618                 default : da[c.name] = ""; break;
5619             }
5620             
5621         });
5622         return new this.recordType(Roo.apply(da, d));
5623     }
5624     
5625 };/*
5626  * Based on:
5627  * Ext JS Library 1.1.1
5628  * Copyright(c) 2006-2007, Ext JS, LLC.
5629  *
5630  * Originally Released Under LGPL - original licence link has changed is not relivant.
5631  *
5632  * Fork - LGPL
5633  * <script type="text/javascript">
5634  */
5635
5636 /**
5637  * @class Roo.data.DataProxy
5638  * @extends Roo.data.Observable
5639  * This class is an abstract base class for implementations which provide retrieval of
5640  * unformatted data objects.<br>
5641  * <p>
5642  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
5643  * (of the appropriate type which knows how to parse the data object) to provide a block of
5644  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
5645  * <p>
5646  * Custom implementations must implement the load method as described in
5647  * {@link Roo.data.HttpProxy#load}.
5648  */
5649 Roo.data.DataProxy = function(){
5650     this.addEvents({
5651         /**
5652          * @event beforeload
5653          * Fires before a network request is made to retrieve a data object.
5654          * @param {Object} This DataProxy object.
5655          * @param {Object} params The params parameter to the load function.
5656          */
5657         beforeload : true,
5658         /**
5659          * @event load
5660          * Fires before the load method's callback is called.
5661          * @param {Object} This DataProxy object.
5662          * @param {Object} o The data object.
5663          * @param {Object} arg The callback argument object passed to the load function.
5664          */
5665         load : true,
5666         /**
5667          * @event loadexception
5668          * Fires if an Exception occurs during data retrieval.
5669          * @param {Object} This DataProxy object.
5670          * @param {Object} o The data object.
5671          * @param {Object} arg The callback argument object passed to the load function.
5672          * @param {Object} e The Exception.
5673          */
5674         loadexception : true
5675     });
5676     Roo.data.DataProxy.superclass.constructor.call(this);
5677 };
5678
5679 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
5680
5681     /**
5682      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
5683      */
5684 /*
5685  * Based on:
5686  * Ext JS Library 1.1.1
5687  * Copyright(c) 2006-2007, Ext JS, LLC.
5688  *
5689  * Originally Released Under LGPL - original licence link has changed is not relivant.
5690  *
5691  * Fork - LGPL
5692  * <script type="text/javascript">
5693  */
5694 /**
5695  * @class Roo.data.MemoryProxy
5696  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
5697  * to the Reader when its load method is called.
5698  * @constructor
5699  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
5700  */
5701 Roo.data.MemoryProxy = function(data){
5702     if (data.data) {
5703         data = data.data;
5704     }
5705     Roo.data.MemoryProxy.superclass.constructor.call(this);
5706     this.data = data;
5707 };
5708
5709 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
5710     /**
5711      * Load data from the requested source (in this case an in-memory
5712      * data object passed to the constructor), read the data object into
5713      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5714      * process that block using the passed callback.
5715      * @param {Object} params This parameter is not used by the MemoryProxy class.
5716      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5717      * object into a block of Roo.data.Records.
5718      * @param {Function} callback The function into which to pass the block of Roo.data.records.
5719      * The function must be passed <ul>
5720      * <li>The Record block object</li>
5721      * <li>The "arg" argument from the load function</li>
5722      * <li>A boolean success indicator</li>
5723      * </ul>
5724      * @param {Object} scope The scope in which to call the callback
5725      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5726      */
5727     load : function(params, reader, callback, scope, arg){
5728         params = params || {};
5729         var result;
5730         try {
5731             result = reader.readRecords(this.data);
5732         }catch(e){
5733             this.fireEvent("loadexception", this, arg, null, e);
5734             callback.call(scope, null, arg, false);
5735             return;
5736         }
5737         callback.call(scope, result, arg, true);
5738     },
5739     
5740     // private
5741     update : function(params, records){
5742         
5743     }
5744 });/*
5745  * Based on:
5746  * Ext JS Library 1.1.1
5747  * Copyright(c) 2006-2007, Ext JS, LLC.
5748  *
5749  * Originally Released Under LGPL - original licence link has changed is not relivant.
5750  *
5751  * Fork - LGPL
5752  * <script type="text/javascript">
5753  */
5754 /**
5755  * @class Roo.data.HttpProxy
5756  * @extends Roo.data.DataProxy
5757  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
5758  * configured to reference a certain URL.<br><br>
5759  * <p>
5760  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
5761  * from which the running page was served.<br><br>
5762  * <p>
5763  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
5764  * <p>
5765  * Be aware that to enable the browser to parse an XML document, the server must set
5766  * the Content-Type header in the HTTP response to "text/xml".
5767  * @constructor
5768  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
5769  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
5770  * will be used to make the request.
5771  */
5772 Roo.data.HttpProxy = function(conn){
5773     Roo.data.HttpProxy.superclass.constructor.call(this);
5774     // is conn a conn config or a real conn?
5775     this.conn = conn;
5776     this.useAjax = !conn || !conn.events;
5777   
5778 };
5779
5780 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
5781     // thse are take from connection...
5782     
5783     /**
5784      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
5785      */
5786     /**
5787      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
5788      * extra parameters to each request made by this object. (defaults to undefined)
5789      */
5790     /**
5791      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
5792      *  to each request made by this object. (defaults to undefined)
5793      */
5794     /**
5795      * @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)
5796      */
5797     /**
5798      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
5799      */
5800      /**
5801      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
5802      * @type Boolean
5803      */
5804   
5805
5806     /**
5807      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
5808      * @type Boolean
5809      */
5810     /**
5811      * Return the {@link Roo.data.Connection} object being used by this Proxy.
5812      * @return {Connection} The Connection object. This object may be used to subscribe to events on
5813      * a finer-grained basis than the DataProxy events.
5814      */
5815     getConnection : function(){
5816         return this.useAjax ? Roo.Ajax : this.conn;
5817     },
5818
5819     /**
5820      * Load data from the configured {@link Roo.data.Connection}, read the data object into
5821      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
5822      * process that block using the passed callback.
5823      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5824      * for the request to the remote server.
5825      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5826      * object into a block of Roo.data.Records.
5827      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5828      * The function must be passed <ul>
5829      * <li>The Record block object</li>
5830      * <li>The "arg" argument from the load function</li>
5831      * <li>A boolean success indicator</li>
5832      * </ul>
5833      * @param {Object} scope The scope in which to call the callback
5834      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5835      */
5836     load : function(params, reader, callback, scope, arg){
5837         if(this.fireEvent("beforeload", this, params) !== false){
5838             var  o = {
5839                 params : params || {},
5840                 request: {
5841                     callback : callback,
5842                     scope : scope,
5843                     arg : arg
5844                 },
5845                 reader: reader,
5846                 callback : this.loadResponse,
5847                 scope: this
5848             };
5849             if(this.useAjax){
5850                 Roo.applyIf(o, this.conn);
5851                 if(this.activeRequest){
5852                     Roo.Ajax.abort(this.activeRequest);
5853                 }
5854                 this.activeRequest = Roo.Ajax.request(o);
5855             }else{
5856                 this.conn.request(o);
5857             }
5858         }else{
5859             callback.call(scope||this, null, arg, false);
5860         }
5861     },
5862
5863     // private
5864     loadResponse : function(o, success, response){
5865         delete this.activeRequest;
5866         if(!success){
5867             this.fireEvent("loadexception", this, o, response);
5868             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5869             return;
5870         }
5871         var result;
5872         try {
5873             result = o.reader.read(response);
5874         }catch(e){
5875             this.fireEvent("loadexception", this, o, response, e);
5876             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5877             return;
5878         }
5879         
5880         this.fireEvent("load", this, o, o.request.arg);
5881         o.request.callback.call(o.request.scope, result, o.request.arg, true);
5882     },
5883
5884     // private
5885     update : function(dataSet){
5886
5887     },
5888
5889     // private
5890     updateResponse : function(dataSet){
5891
5892     }
5893 });/*
5894  * Based on:
5895  * Ext JS Library 1.1.1
5896  * Copyright(c) 2006-2007, Ext JS, LLC.
5897  *
5898  * Originally Released Under LGPL - original licence link has changed is not relivant.
5899  *
5900  * Fork - LGPL
5901  * <script type="text/javascript">
5902  */
5903
5904 /**
5905  * @class Roo.data.ScriptTagProxy
5906  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
5907  * other than the originating domain of the running page.<br><br>
5908  * <p>
5909  * <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
5910  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
5911  * <p>
5912  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
5913  * source code that is used as the source inside a &lt;script> tag.<br><br>
5914  * <p>
5915  * In order for the browser to process the returned data, the server must wrap the data object
5916  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
5917  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
5918  * depending on whether the callback name was passed:
5919  * <p>
5920  * <pre><code>
5921 boolean scriptTag = false;
5922 String cb = request.getParameter("callback");
5923 if (cb != null) {
5924     scriptTag = true;
5925     response.setContentType("text/javascript");
5926 } else {
5927     response.setContentType("application/x-json");
5928 }
5929 Writer out = response.getWriter();
5930 if (scriptTag) {
5931     out.write(cb + "(");
5932 }
5933 out.print(dataBlock.toJsonString());
5934 if (scriptTag) {
5935     out.write(");");
5936 }
5937 </pre></code>
5938  *
5939  * @constructor
5940  * @param {Object} config A configuration object.
5941  */
5942 Roo.data.ScriptTagProxy = function(config){
5943     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
5944     Roo.apply(this, config);
5945     this.head = document.getElementsByTagName("head")[0];
5946 };
5947
5948 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
5949
5950 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
5951     /**
5952      * @cfg {String} url The URL from which to request the data object.
5953      */
5954     /**
5955      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
5956      */
5957     timeout : 30000,
5958     /**
5959      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
5960      * the server the name of the callback function set up by the load call to process the returned data object.
5961      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
5962      * javascript output which calls this named function passing the data object as its only parameter.
5963      */
5964     callbackParam : "callback",
5965     /**
5966      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
5967      * name to the request.
5968      */
5969     nocache : true,
5970
5971     /**
5972      * Load data from the configured URL, read the data object into
5973      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5974      * process that block using the passed callback.
5975      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5976      * for the request to the remote server.
5977      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5978      * object into a block of Roo.data.Records.
5979      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5980      * The function must be passed <ul>
5981      * <li>The Record block object</li>
5982      * <li>The "arg" argument from the load function</li>
5983      * <li>A boolean success indicator</li>
5984      * </ul>
5985      * @param {Object} scope The scope in which to call the callback
5986      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5987      */
5988     load : function(params, reader, callback, scope, arg){
5989         if(this.fireEvent("beforeload", this, params) !== false){
5990
5991             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
5992
5993             var url = this.url;
5994             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
5995             if(this.nocache){
5996                 url += "&_dc=" + (new Date().getTime());
5997             }
5998             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
5999             var trans = {
6000                 id : transId,
6001                 cb : "stcCallback"+transId,
6002                 scriptId : "stcScript"+transId,
6003                 params : params,
6004                 arg : arg,
6005                 url : url,
6006                 callback : callback,
6007                 scope : scope,
6008                 reader : reader
6009             };
6010             var conn = this;
6011
6012             window[trans.cb] = function(o){
6013                 conn.handleResponse(o, trans);
6014             };
6015
6016             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
6017
6018             if(this.autoAbort !== false){
6019                 this.abort();
6020             }
6021
6022             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
6023
6024             var script = document.createElement("script");
6025             script.setAttribute("src", url);
6026             script.setAttribute("type", "text/javascript");
6027             script.setAttribute("id", trans.scriptId);
6028             this.head.appendChild(script);
6029
6030             this.trans = trans;
6031         }else{
6032             callback.call(scope||this, null, arg, false);
6033         }
6034     },
6035
6036     // private
6037     isLoading : function(){
6038         return this.trans ? true : false;
6039     },
6040
6041     /**
6042      * Abort the current server request.
6043      */
6044     abort : function(){
6045         if(this.isLoading()){
6046             this.destroyTrans(this.trans);
6047         }
6048     },
6049
6050     // private
6051     destroyTrans : function(trans, isLoaded){
6052         this.head.removeChild(document.getElementById(trans.scriptId));
6053         clearTimeout(trans.timeoutId);
6054         if(isLoaded){
6055             window[trans.cb] = undefined;
6056             try{
6057                 delete window[trans.cb];
6058             }catch(e){}
6059         }else{
6060             // if hasn't been loaded, wait for load to remove it to prevent script error
6061             window[trans.cb] = function(){
6062                 window[trans.cb] = undefined;
6063                 try{
6064                     delete window[trans.cb];
6065                 }catch(e){}
6066             };
6067         }
6068     },
6069
6070     // private
6071     handleResponse : function(o, trans){
6072         this.trans = false;
6073         this.destroyTrans(trans, true);
6074         var result;
6075         try {
6076             result = trans.reader.readRecords(o);
6077         }catch(e){
6078             this.fireEvent("loadexception", this, o, trans.arg, e);
6079             trans.callback.call(trans.scope||window, null, trans.arg, false);
6080             return;
6081         }
6082         this.fireEvent("load", this, o, trans.arg);
6083         trans.callback.call(trans.scope||window, result, trans.arg, true);
6084     },
6085
6086     // private
6087     handleFailure : function(trans){
6088         this.trans = false;
6089         this.destroyTrans(trans, false);
6090         this.fireEvent("loadexception", this, null, trans.arg);
6091         trans.callback.call(trans.scope||window, null, trans.arg, false);
6092     }
6093 });/*
6094  * Based on:
6095  * Ext JS Library 1.1.1
6096  * Copyright(c) 2006-2007, Ext JS, LLC.
6097  *
6098  * Originally Released Under LGPL - original licence link has changed is not relivant.
6099  *
6100  * Fork - LGPL
6101  * <script type="text/javascript">
6102  */
6103
6104 /**
6105  * @class Roo.data.JsonReader
6106  * @extends Roo.data.DataReader
6107  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
6108  * based on mappings in a provided Roo.data.Record constructor.
6109  * 
6110  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
6111  * in the reply previously. 
6112  * 
6113  * <p>
6114  * Example code:
6115  * <pre><code>
6116 var RecordDef = Roo.data.Record.create([
6117     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6118     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6119 ]);
6120 var myReader = new Roo.data.JsonReader({
6121     totalProperty: "results",    // The property which contains the total dataset size (optional)
6122     root: "rows",                // The property which contains an Array of row objects
6123     id: "id"                     // The property within each row object that provides an ID for the record (optional)
6124 }, RecordDef);
6125 </code></pre>
6126  * <p>
6127  * This would consume a JSON file like this:
6128  * <pre><code>
6129 { 'results': 2, 'rows': [
6130     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
6131     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
6132 }
6133 </code></pre>
6134  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
6135  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6136  * paged from the remote server.
6137  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
6138  * @cfg {String} root name of the property which contains the Array of row objects.
6139  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
6140  * @constructor
6141  * Create a new JsonReader
6142  * @param {Object} meta Metadata configuration options
6143  * @param {Object} recordType Either an Array of field definition objects,
6144  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
6145  */
6146 Roo.data.JsonReader = function(meta, recordType){
6147     
6148     meta = meta || {};
6149     // set some defaults:
6150     Roo.applyIf(meta, {
6151         totalProperty: 'total',
6152         successProperty : 'success',
6153         root : 'data',
6154         id : 'id'
6155     });
6156     
6157     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6158 };
6159 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
6160     
6161     /**
6162      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
6163      * Used by Store query builder to append _requestMeta to params.
6164      * 
6165      */
6166     metaFromRemote : false,
6167     /**
6168      * This method is only used by a DataProxy which has retrieved data from a remote server.
6169      * @param {Object} response The XHR object which contains the JSON data in its responseText.
6170      * @return {Object} data A data block which is used by an Roo.data.Store object as
6171      * a cache of Roo.data.Records.
6172      */
6173     read : function(response){
6174         var json = response.responseText;
6175        
6176         var o = /* eval:var:o */ eval("("+json+")");
6177         if(!o) {
6178             throw {message: "JsonReader.read: Json object not found"};
6179         }
6180         
6181         if(o.metaData){
6182             
6183             delete this.ef;
6184             this.metaFromRemote = true;
6185             this.meta = o.metaData;
6186             this.recordType = Roo.data.Record.create(o.metaData.fields);
6187             this.onMetaChange(this.meta, this.recordType, o);
6188         }
6189         return this.readRecords(o);
6190     },
6191
6192     // private function a store will implement
6193     onMetaChange : function(meta, recordType, o){
6194
6195     },
6196
6197     /**
6198          * @ignore
6199          */
6200     simpleAccess: function(obj, subsc) {
6201         return obj[subsc];
6202     },
6203
6204         /**
6205          * @ignore
6206          */
6207     getJsonAccessor: function(){
6208         var re = /[\[\.]/;
6209         return function(expr) {
6210             try {
6211                 return(re.test(expr))
6212                     ? new Function("obj", "return obj." + expr)
6213                     : function(obj){
6214                         return obj[expr];
6215                     };
6216             } catch(e){}
6217             return Roo.emptyFn;
6218         };
6219     }(),
6220
6221     /**
6222      * Create a data block containing Roo.data.Records from an XML document.
6223      * @param {Object} o An object which contains an Array of row objects in the property specified
6224      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
6225      * which contains the total size of the dataset.
6226      * @return {Object} data A data block which is used by an Roo.data.Store object as
6227      * a cache of Roo.data.Records.
6228      */
6229     readRecords : function(o){
6230         /**
6231          * After any data loads, the raw JSON data is available for further custom processing.
6232          * @type Object
6233          */
6234         this.o = o;
6235         var s = this.meta, Record = this.recordType,
6236             f = Record.prototype.fields, fi = f.items, fl = f.length;
6237
6238 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
6239         if (!this.ef) {
6240             if(s.totalProperty) {
6241                     this.getTotal = this.getJsonAccessor(s.totalProperty);
6242                 }
6243                 if(s.successProperty) {
6244                     this.getSuccess = this.getJsonAccessor(s.successProperty);
6245                 }
6246                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
6247                 if (s.id) {
6248                         var g = this.getJsonAccessor(s.id);
6249                         this.getId = function(rec) {
6250                                 var r = g(rec);
6251                                 return (r === undefined || r === "") ? null : r;
6252                         };
6253                 } else {
6254                         this.getId = function(){return null;};
6255                 }
6256             this.ef = [];
6257             for(var jj = 0; jj < fl; jj++){
6258                 f = fi[jj];
6259                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
6260                 this.ef[jj] = this.getJsonAccessor(map);
6261             }
6262         }
6263
6264         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
6265         if(s.totalProperty){
6266             var vt = parseInt(this.getTotal(o), 10);
6267             if(!isNaN(vt)){
6268                 totalRecords = vt;
6269             }
6270         }
6271         if(s.successProperty){
6272             var vs = this.getSuccess(o);
6273             if(vs === false || vs === 'false'){
6274                 success = false;
6275             }
6276         }
6277         var records = [];
6278             for(var i = 0; i < c; i++){
6279                     var n = root[i];
6280                 var values = {};
6281                 var id = this.getId(n);
6282                 for(var j = 0; j < fl; j++){
6283                     f = fi[j];
6284                 var v = this.ef[j](n);
6285                 if (!f.convert) {
6286                     Roo.log('missing convert for ' + f.name);
6287                     Roo.log(f);
6288                     continue;
6289                 }
6290                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
6291                 }
6292                 var record = new Record(values, id);
6293                 record.json = n;
6294                 records[i] = record;
6295             }
6296             return {
6297             raw : o,
6298                 success : success,
6299                 records : records,
6300                 totalRecords : totalRecords
6301             };
6302     }
6303 });/*
6304  * Based on:
6305  * Ext JS Library 1.1.1
6306  * Copyright(c) 2006-2007, Ext JS, LLC.
6307  *
6308  * Originally Released Under LGPL - original licence link has changed is not relivant.
6309  *
6310  * Fork - LGPL
6311  * <script type="text/javascript">
6312  */
6313
6314 /**
6315  * @class Roo.data.XmlReader
6316  * @extends Roo.data.DataReader
6317  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
6318  * based on mappings in a provided Roo.data.Record constructor.<br><br>
6319  * <p>
6320  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
6321  * header in the HTTP response must be set to "text/xml".</em>
6322  * <p>
6323  * Example code:
6324  * <pre><code>
6325 var RecordDef = Roo.data.Record.create([
6326    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6327    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6328 ]);
6329 var myReader = new Roo.data.XmlReader({
6330    totalRecords: "results", // The element which contains the total dataset size (optional)
6331    record: "row",           // The repeated element which contains row information
6332    id: "id"                 // The element within the row that provides an ID for the record (optional)
6333 }, RecordDef);
6334 </code></pre>
6335  * <p>
6336  * This would consume an XML file like this:
6337  * <pre><code>
6338 &lt;?xml?>
6339 &lt;dataset>
6340  &lt;results>2&lt;/results>
6341  &lt;row>
6342    &lt;id>1&lt;/id>
6343    &lt;name>Bill&lt;/name>
6344    &lt;occupation>Gardener&lt;/occupation>
6345  &lt;/row>
6346  &lt;row>
6347    &lt;id>2&lt;/id>
6348    &lt;name>Ben&lt;/name>
6349    &lt;occupation>Horticulturalist&lt;/occupation>
6350  &lt;/row>
6351 &lt;/dataset>
6352 </code></pre>
6353  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
6354  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6355  * paged from the remote server.
6356  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
6357  * @cfg {String} success The DomQuery path to the success attribute used by forms.
6358  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
6359  * a record identifier value.
6360  * @constructor
6361  * Create a new XmlReader
6362  * @param {Object} meta Metadata configuration options
6363  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
6364  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
6365  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
6366  */
6367 Roo.data.XmlReader = function(meta, recordType){
6368     meta = meta || {};
6369     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6370 };
6371 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
6372     /**
6373      * This method is only used by a DataProxy which has retrieved data from a remote server.
6374          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
6375          * to contain a method called 'responseXML' that returns an XML document object.
6376      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6377      * a cache of Roo.data.Records.
6378      */
6379     read : function(response){
6380         var doc = response.responseXML;
6381         if(!doc) {
6382             throw {message: "XmlReader.read: XML Document not available"};
6383         }
6384         return this.readRecords(doc);
6385     },
6386
6387     /**
6388      * Create a data block containing Roo.data.Records from an XML document.
6389          * @param {Object} doc A parsed XML document.
6390      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6391      * a cache of Roo.data.Records.
6392      */
6393     readRecords : function(doc){
6394         /**
6395          * After any data loads/reads, the raw XML Document is available for further custom processing.
6396          * @type XMLDocument
6397          */
6398         this.xmlData = doc;
6399         var root = doc.documentElement || doc;
6400         var q = Roo.DomQuery;
6401         var recordType = this.recordType, fields = recordType.prototype.fields;
6402         var sid = this.meta.id;
6403         var totalRecords = 0, success = true;
6404         if(this.meta.totalRecords){
6405             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
6406         }
6407         
6408         if(this.meta.success){
6409             var sv = q.selectValue(this.meta.success, root, true);
6410             success = sv !== false && sv !== 'false';
6411         }
6412         var records = [];
6413         var ns = q.select(this.meta.record, root);
6414         for(var i = 0, len = ns.length; i < len; i++) {
6415                 var n = ns[i];
6416                 var values = {};
6417                 var id = sid ? q.selectValue(sid, n) : undefined;
6418                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6419                     var f = fields.items[j];
6420                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
6421                     v = f.convert(v);
6422                     values[f.name] = v;
6423                 }
6424                 var record = new recordType(values, id);
6425                 record.node = n;
6426                 records[records.length] = record;
6427             }
6428
6429             return {
6430                 success : success,
6431                 records : records,
6432                 totalRecords : totalRecords || records.length
6433             };
6434     }
6435 });/*
6436  * Based on:
6437  * Ext JS Library 1.1.1
6438  * Copyright(c) 2006-2007, Ext JS, LLC.
6439  *
6440  * Originally Released Under LGPL - original licence link has changed is not relivant.
6441  *
6442  * Fork - LGPL
6443  * <script type="text/javascript">
6444  */
6445
6446 /**
6447  * @class Roo.data.ArrayReader
6448  * @extends Roo.data.DataReader
6449  * Data reader class to create an Array of Roo.data.Record objects from an Array.
6450  * Each element of that Array represents a row of data fields. The
6451  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
6452  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
6453  * <p>
6454  * Example code:.
6455  * <pre><code>
6456 var RecordDef = Roo.data.Record.create([
6457     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
6458     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
6459 ]);
6460 var myReader = new Roo.data.ArrayReader({
6461     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
6462 }, RecordDef);
6463 </code></pre>
6464  * <p>
6465  * This would consume an Array like this:
6466  * <pre><code>
6467 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
6468   </code></pre>
6469  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
6470  * @constructor
6471  * Create a new JsonReader
6472  * @param {Object} meta Metadata configuration options.
6473  * @param {Object} recordType Either an Array of field definition objects
6474  * as specified to {@link Roo.data.Record#create},
6475  * or an {@link Roo.data.Record} object
6476  * created using {@link Roo.data.Record#create}.
6477  */
6478 Roo.data.ArrayReader = function(meta, recordType){
6479     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
6480 };
6481
6482 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
6483     /**
6484      * Create a data block containing Roo.data.Records from an XML document.
6485      * @param {Object} o An Array of row objects which represents the dataset.
6486      * @return {Object} data A data block which is used by an Roo.data.Store object as
6487      * a cache of Roo.data.Records.
6488      */
6489     readRecords : function(o){
6490         var sid = this.meta ? this.meta.id : null;
6491         var recordType = this.recordType, fields = recordType.prototype.fields;
6492         var records = [];
6493         var root = o;
6494             for(var i = 0; i < root.length; i++){
6495                     var n = root[i];
6496                 var values = {};
6497                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
6498                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6499                 var f = fields.items[j];
6500                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
6501                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
6502                 v = f.convert(v);
6503                 values[f.name] = v;
6504             }
6505                 var record = new recordType(values, id);
6506                 record.json = n;
6507                 records[records.length] = record;
6508             }
6509             return {
6510                 records : records,
6511                 totalRecords : records.length
6512             };
6513     }
6514 });/*
6515  * Based on:
6516  * Ext JS Library 1.1.1
6517  * Copyright(c) 2006-2007, Ext JS, LLC.
6518  *
6519  * Originally Released Under LGPL - original licence link has changed is not relivant.
6520  *
6521  * Fork - LGPL
6522  * <script type="text/javascript">
6523  */
6524
6525
6526 /**
6527  * @class Roo.data.Tree
6528  * @extends Roo.util.Observable
6529  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
6530  * in the tree have most standard DOM functionality.
6531  * @constructor
6532  * @param {Node} root (optional) The root node
6533  */
6534 Roo.data.Tree = function(root){
6535    this.nodeHash = {};
6536    /**
6537     * The root node for this tree
6538     * @type Node
6539     */
6540    this.root = null;
6541    if(root){
6542        this.setRootNode(root);
6543    }
6544    this.addEvents({
6545        /**
6546         * @event append
6547         * Fires when a new child node is appended to a node in this tree.
6548         * @param {Tree} tree The owner tree
6549         * @param {Node} parent The parent node
6550         * @param {Node} node The newly appended node
6551         * @param {Number} index The index of the newly appended node
6552         */
6553        "append" : true,
6554        /**
6555         * @event remove
6556         * Fires when a child node is removed from a node in this tree.
6557         * @param {Tree} tree The owner tree
6558         * @param {Node} parent The parent node
6559         * @param {Node} node The child node removed
6560         */
6561        "remove" : true,
6562        /**
6563         * @event move
6564         * Fires when a node is moved to a new location in the tree
6565         * @param {Tree} tree The owner tree
6566         * @param {Node} node The node moved
6567         * @param {Node} oldParent The old parent of this node
6568         * @param {Node} newParent The new parent of this node
6569         * @param {Number} index The index it was moved to
6570         */
6571        "move" : true,
6572        /**
6573         * @event insert
6574         * Fires when a new child node is inserted in a node in this tree.
6575         * @param {Tree} tree The owner tree
6576         * @param {Node} parent The parent node
6577         * @param {Node} node The child node inserted
6578         * @param {Node} refNode The child node the node was inserted before
6579         */
6580        "insert" : true,
6581        /**
6582         * @event beforeappend
6583         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
6584         * @param {Tree} tree The owner tree
6585         * @param {Node} parent The parent node
6586         * @param {Node} node The child node to be appended
6587         */
6588        "beforeappend" : true,
6589        /**
6590         * @event beforeremove
6591         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
6592         * @param {Tree} tree The owner tree
6593         * @param {Node} parent The parent node
6594         * @param {Node} node The child node to be removed
6595         */
6596        "beforeremove" : true,
6597        /**
6598         * @event beforemove
6599         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
6600         * @param {Tree} tree The owner tree
6601         * @param {Node} node The node being moved
6602         * @param {Node} oldParent The parent of the node
6603         * @param {Node} newParent The new parent the node is moving to
6604         * @param {Number} index The index it is being moved to
6605         */
6606        "beforemove" : true,
6607        /**
6608         * @event beforeinsert
6609         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
6610         * @param {Tree} tree The owner tree
6611         * @param {Node} parent The parent node
6612         * @param {Node} node The child node to be inserted
6613         * @param {Node} refNode The child node the node is being inserted before
6614         */
6615        "beforeinsert" : true
6616    });
6617
6618     Roo.data.Tree.superclass.constructor.call(this);
6619 };
6620
6621 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
6622     pathSeparator: "/",
6623
6624     proxyNodeEvent : function(){
6625         return this.fireEvent.apply(this, arguments);
6626     },
6627
6628     /**
6629      * Returns the root node for this tree.
6630      * @return {Node}
6631      */
6632     getRootNode : function(){
6633         return this.root;
6634     },
6635
6636     /**
6637      * Sets the root node for this tree.
6638      * @param {Node} node
6639      * @return {Node}
6640      */
6641     setRootNode : function(node){
6642         this.root = node;
6643         node.ownerTree = this;
6644         node.isRoot = true;
6645         this.registerNode(node);
6646         return node;
6647     },
6648
6649     /**
6650      * Gets a node in this tree by its id.
6651      * @param {String} id
6652      * @return {Node}
6653      */
6654     getNodeById : function(id){
6655         return this.nodeHash[id];
6656     },
6657
6658     registerNode : function(node){
6659         this.nodeHash[node.id] = node;
6660     },
6661
6662     unregisterNode : function(node){
6663         delete this.nodeHash[node.id];
6664     },
6665
6666     toString : function(){
6667         return "[Tree"+(this.id?" "+this.id:"")+"]";
6668     }
6669 });
6670
6671 /**
6672  * @class Roo.data.Node
6673  * @extends Roo.util.Observable
6674  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
6675  * @cfg {String} id The id for this node. If one is not specified, one is generated.
6676  * @constructor
6677  * @param {Object} attributes The attributes/config for the node
6678  */
6679 Roo.data.Node = function(attributes){
6680     /**
6681      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
6682      * @type {Object}
6683      */
6684     this.attributes = attributes || {};
6685     this.leaf = this.attributes.leaf;
6686     /**
6687      * The node id. @type String
6688      */
6689     this.id = this.attributes.id;
6690     if(!this.id){
6691         this.id = Roo.id(null, "ynode-");
6692         this.attributes.id = this.id;
6693     }
6694      
6695     
6696     /**
6697      * All child nodes of this node. @type Array
6698      */
6699     this.childNodes = [];
6700     if(!this.childNodes.indexOf){ // indexOf is a must
6701         this.childNodes.indexOf = function(o){
6702             for(var i = 0, len = this.length; i < len; i++){
6703                 if(this[i] == o) {
6704                     return i;
6705                 }
6706             }
6707             return -1;
6708         };
6709     }
6710     /**
6711      * The parent node for this node. @type Node
6712      */
6713     this.parentNode = null;
6714     /**
6715      * The first direct child node of this node, or null if this node has no child nodes. @type Node
6716      */
6717     this.firstChild = null;
6718     /**
6719      * The last direct child node of this node, or null if this node has no child nodes. @type Node
6720      */
6721     this.lastChild = null;
6722     /**
6723      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
6724      */
6725     this.previousSibling = null;
6726     /**
6727      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
6728      */
6729     this.nextSibling = null;
6730
6731     this.addEvents({
6732        /**
6733         * @event append
6734         * Fires when a new child node is appended
6735         * @param {Tree} tree The owner tree
6736         * @param {Node} this This node
6737         * @param {Node} node The newly appended node
6738         * @param {Number} index The index of the newly appended node
6739         */
6740        "append" : true,
6741        /**
6742         * @event remove
6743         * Fires when a child node is removed
6744         * @param {Tree} tree The owner tree
6745         * @param {Node} this This node
6746         * @param {Node} node The removed node
6747         */
6748        "remove" : true,
6749        /**
6750         * @event move
6751         * Fires when this node is moved to a new location in the tree
6752         * @param {Tree} tree The owner tree
6753         * @param {Node} this This node
6754         * @param {Node} oldParent The old parent of this node
6755         * @param {Node} newParent The new parent of this node
6756         * @param {Number} index The index it was moved to
6757         */
6758        "move" : true,
6759        /**
6760         * @event insert
6761         * Fires when a new child node is inserted.
6762         * @param {Tree} tree The owner tree
6763         * @param {Node} this This node
6764         * @param {Node} node The child node inserted
6765         * @param {Node} refNode The child node the node was inserted before
6766         */
6767        "insert" : true,
6768        /**
6769         * @event beforeappend
6770         * Fires before a new child is appended, return false to cancel the append.
6771         * @param {Tree} tree The owner tree
6772         * @param {Node} this This node
6773         * @param {Node} node The child node to be appended
6774         */
6775        "beforeappend" : true,
6776        /**
6777         * @event beforeremove
6778         * Fires before a child is removed, return false to cancel the remove.
6779         * @param {Tree} tree The owner tree
6780         * @param {Node} this This node
6781         * @param {Node} node The child node to be removed
6782         */
6783        "beforeremove" : true,
6784        /**
6785         * @event beforemove
6786         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
6787         * @param {Tree} tree The owner tree
6788         * @param {Node} this This node
6789         * @param {Node} oldParent The parent of this node
6790         * @param {Node} newParent The new parent this node is moving to
6791         * @param {Number} index The index it is being moved to
6792         */
6793        "beforemove" : true,
6794        /**
6795         * @event beforeinsert
6796         * Fires before a new child is inserted, return false to cancel the insert.
6797         * @param {Tree} tree The owner tree
6798         * @param {Node} this This node
6799         * @param {Node} node The child node to be inserted
6800         * @param {Node} refNode The child node the node is being inserted before
6801         */
6802        "beforeinsert" : true
6803    });
6804     this.listeners = this.attributes.listeners;
6805     Roo.data.Node.superclass.constructor.call(this);
6806 };
6807
6808 Roo.extend(Roo.data.Node, Roo.util.Observable, {
6809     fireEvent : function(evtName){
6810         // first do standard event for this node
6811         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
6812             return false;
6813         }
6814         // then bubble it up to the tree if the event wasn't cancelled
6815         var ot = this.getOwnerTree();
6816         if(ot){
6817             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
6818                 return false;
6819             }
6820         }
6821         return true;
6822     },
6823
6824     /**
6825      * Returns true if this node is a leaf
6826      * @return {Boolean}
6827      */
6828     isLeaf : function(){
6829         return this.leaf === true;
6830     },
6831
6832     // private
6833     setFirstChild : function(node){
6834         this.firstChild = node;
6835     },
6836
6837     //private
6838     setLastChild : function(node){
6839         this.lastChild = node;
6840     },
6841
6842
6843     /**
6844      * Returns true if this node is the last child of its parent
6845      * @return {Boolean}
6846      */
6847     isLast : function(){
6848        return (!this.parentNode ? true : this.parentNode.lastChild == this);
6849     },
6850
6851     /**
6852      * Returns true if this node is the first child of its parent
6853      * @return {Boolean}
6854      */
6855     isFirst : function(){
6856        return (!this.parentNode ? true : this.parentNode.firstChild == this);
6857     },
6858
6859     hasChildNodes : function(){
6860         return !this.isLeaf() && this.childNodes.length > 0;
6861     },
6862
6863     /**
6864      * Insert node(s) as the last child node of this node.
6865      * @param {Node/Array} node The node or Array of nodes to append
6866      * @return {Node} The appended node if single append, or null if an array was passed
6867      */
6868     appendChild : function(node){
6869         var multi = false;
6870         if(node instanceof Array){
6871             multi = node;
6872         }else if(arguments.length > 1){
6873             multi = arguments;
6874         }
6875         // if passed an array or multiple args do them one by one
6876         if(multi){
6877             for(var i = 0, len = multi.length; i < len; i++) {
6878                 this.appendChild(multi[i]);
6879             }
6880         }else{
6881             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
6882                 return false;
6883             }
6884             var index = this.childNodes.length;
6885             var oldParent = node.parentNode;
6886             // it's a move, make sure we move it cleanly
6887             if(oldParent){
6888                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
6889                     return false;
6890                 }
6891                 oldParent.removeChild(node);
6892             }
6893             index = this.childNodes.length;
6894             if(index == 0){
6895                 this.setFirstChild(node);
6896             }
6897             this.childNodes.push(node);
6898             node.parentNode = this;
6899             var ps = this.childNodes[index-1];
6900             if(ps){
6901                 node.previousSibling = ps;
6902                 ps.nextSibling = node;
6903             }else{
6904                 node.previousSibling = null;
6905             }
6906             node.nextSibling = null;
6907             this.setLastChild(node);
6908             node.setOwnerTree(this.getOwnerTree());
6909             this.fireEvent("append", this.ownerTree, this, node, index);
6910             if(oldParent){
6911                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
6912             }
6913             return node;
6914         }
6915     },
6916
6917     /**
6918      * Removes a child node from this node.
6919      * @param {Node} node The node to remove
6920      * @return {Node} The removed node
6921      */
6922     removeChild : function(node){
6923         var index = this.childNodes.indexOf(node);
6924         if(index == -1){
6925             return false;
6926         }
6927         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
6928             return false;
6929         }
6930
6931         // remove it from childNodes collection
6932         this.childNodes.splice(index, 1);
6933
6934         // update siblings
6935         if(node.previousSibling){
6936             node.previousSibling.nextSibling = node.nextSibling;
6937         }
6938         if(node.nextSibling){
6939             node.nextSibling.previousSibling = node.previousSibling;
6940         }
6941
6942         // update child refs
6943         if(this.firstChild == node){
6944             this.setFirstChild(node.nextSibling);
6945         }
6946         if(this.lastChild == node){
6947             this.setLastChild(node.previousSibling);
6948         }
6949
6950         node.setOwnerTree(null);
6951         // clear any references from the node
6952         node.parentNode = null;
6953         node.previousSibling = null;
6954         node.nextSibling = null;
6955         this.fireEvent("remove", this.ownerTree, this, node);
6956         return node;
6957     },
6958
6959     /**
6960      * Inserts the first node before the second node in this nodes childNodes collection.
6961      * @param {Node} node The node to insert
6962      * @param {Node} refNode The node to insert before (if null the node is appended)
6963      * @return {Node} The inserted node
6964      */
6965     insertBefore : function(node, refNode){
6966         if(!refNode){ // like standard Dom, refNode can be null for append
6967             return this.appendChild(node);
6968         }
6969         // nothing to do
6970         if(node == refNode){
6971             return false;
6972         }
6973
6974         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
6975             return false;
6976         }
6977         var index = this.childNodes.indexOf(refNode);
6978         var oldParent = node.parentNode;
6979         var refIndex = index;
6980
6981         // when moving internally, indexes will change after remove
6982         if(oldParent == this && this.childNodes.indexOf(node) < index){
6983             refIndex--;
6984         }
6985
6986         // it's a move, make sure we move it cleanly
6987         if(oldParent){
6988             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
6989                 return false;
6990             }
6991             oldParent.removeChild(node);
6992         }
6993         if(refIndex == 0){
6994             this.setFirstChild(node);
6995         }
6996         this.childNodes.splice(refIndex, 0, node);
6997         node.parentNode = this;
6998         var ps = this.childNodes[refIndex-1];
6999         if(ps){
7000             node.previousSibling = ps;
7001             ps.nextSibling = node;
7002         }else{
7003             node.previousSibling = null;
7004         }
7005         node.nextSibling = refNode;
7006         refNode.previousSibling = node;
7007         node.setOwnerTree(this.getOwnerTree());
7008         this.fireEvent("insert", this.ownerTree, this, node, refNode);
7009         if(oldParent){
7010             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
7011         }
7012         return node;
7013     },
7014
7015     /**
7016      * Returns the child node at the specified index.
7017      * @param {Number} index
7018      * @return {Node}
7019      */
7020     item : function(index){
7021         return this.childNodes[index];
7022     },
7023
7024     /**
7025      * Replaces one child node in this node with another.
7026      * @param {Node} newChild The replacement node
7027      * @param {Node} oldChild The node to replace
7028      * @return {Node} The replaced node
7029      */
7030     replaceChild : function(newChild, oldChild){
7031         this.insertBefore(newChild, oldChild);
7032         this.removeChild(oldChild);
7033         return oldChild;
7034     },
7035
7036     /**
7037      * Returns the index of a child node
7038      * @param {Node} node
7039      * @return {Number} The index of the node or -1 if it was not found
7040      */
7041     indexOf : function(child){
7042         return this.childNodes.indexOf(child);
7043     },
7044
7045     /**
7046      * Returns the tree this node is in.
7047      * @return {Tree}
7048      */
7049     getOwnerTree : function(){
7050         // if it doesn't have one, look for one
7051         if(!this.ownerTree){
7052             var p = this;
7053             while(p){
7054                 if(p.ownerTree){
7055                     this.ownerTree = p.ownerTree;
7056                     break;
7057                 }
7058                 p = p.parentNode;
7059             }
7060         }
7061         return this.ownerTree;
7062     },
7063
7064     /**
7065      * Returns depth of this node (the root node has a depth of 0)
7066      * @return {Number}
7067      */
7068     getDepth : function(){
7069         var depth = 0;
7070         var p = this;
7071         while(p.parentNode){
7072             ++depth;
7073             p = p.parentNode;
7074         }
7075         return depth;
7076     },
7077
7078     // private
7079     setOwnerTree : function(tree){
7080         // if it's move, we need to update everyone
7081         if(tree != this.ownerTree){
7082             if(this.ownerTree){
7083                 this.ownerTree.unregisterNode(this);
7084             }
7085             this.ownerTree = tree;
7086             var cs = this.childNodes;
7087             for(var i = 0, len = cs.length; i < len; i++) {
7088                 cs[i].setOwnerTree(tree);
7089             }
7090             if(tree){
7091                 tree.registerNode(this);
7092             }
7093         }
7094     },
7095
7096     /**
7097      * Returns the path for this node. The path can be used to expand or select this node programmatically.
7098      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
7099      * @return {String} The path
7100      */
7101     getPath : function(attr){
7102         attr = attr || "id";
7103         var p = this.parentNode;
7104         var b = [this.attributes[attr]];
7105         while(p){
7106             b.unshift(p.attributes[attr]);
7107             p = p.parentNode;
7108         }
7109         var sep = this.getOwnerTree().pathSeparator;
7110         return sep + b.join(sep);
7111     },
7112
7113     /**
7114      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7115      * function call will be the scope provided or the current node. The arguments to the function
7116      * will be the args provided or the current node. If the function returns false at any point,
7117      * the bubble is stopped.
7118      * @param {Function} fn The function to call
7119      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7120      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7121      */
7122     bubble : function(fn, scope, args){
7123         var p = this;
7124         while(p){
7125             if(fn.call(scope || p, args || p) === false){
7126                 break;
7127             }
7128             p = p.parentNode;
7129         }
7130     },
7131
7132     /**
7133      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7134      * function call will be the scope provided or the current node. The arguments to the function
7135      * will be the args provided or the current node. If the function returns false at any point,
7136      * the cascade is stopped on that branch.
7137      * @param {Function} fn The function to call
7138      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7139      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7140      */
7141     cascade : function(fn, scope, args){
7142         if(fn.call(scope || this, args || this) !== false){
7143             var cs = this.childNodes;
7144             for(var i = 0, len = cs.length; i < len; i++) {
7145                 cs[i].cascade(fn, scope, args);
7146             }
7147         }
7148     },
7149
7150     /**
7151      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
7152      * function call will be the scope provided or the current node. The arguments to the function
7153      * will be the args provided or the current node. If the function returns false at any point,
7154      * the iteration stops.
7155      * @param {Function} fn The function to call
7156      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7157      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7158      */
7159     eachChild : function(fn, scope, args){
7160         var cs = this.childNodes;
7161         for(var i = 0, len = cs.length; i < len; i++) {
7162                 if(fn.call(scope || this, args || cs[i]) === false){
7163                     break;
7164                 }
7165         }
7166     },
7167
7168     /**
7169      * Finds the first child that has the attribute with the specified value.
7170      * @param {String} attribute The attribute name
7171      * @param {Mixed} value The value to search for
7172      * @return {Node} The found child or null if none was found
7173      */
7174     findChild : function(attribute, value){
7175         var cs = this.childNodes;
7176         for(var i = 0, len = cs.length; i < len; i++) {
7177                 if(cs[i].attributes[attribute] == value){
7178                     return cs[i];
7179                 }
7180         }
7181         return null;
7182     },
7183
7184     /**
7185      * Finds the first child by a custom function. The child matches if the function passed
7186      * returns true.
7187      * @param {Function} fn
7188      * @param {Object} scope (optional)
7189      * @return {Node} The found child or null if none was found
7190      */
7191     findChildBy : function(fn, scope){
7192         var cs = this.childNodes;
7193         for(var i = 0, len = cs.length; i < len; i++) {
7194                 if(fn.call(scope||cs[i], cs[i]) === true){
7195                     return cs[i];
7196                 }
7197         }
7198         return null;
7199     },
7200
7201     /**
7202      * Sorts this nodes children using the supplied sort function
7203      * @param {Function} fn
7204      * @param {Object} scope (optional)
7205      */
7206     sort : function(fn, scope){
7207         var cs = this.childNodes;
7208         var len = cs.length;
7209         if(len > 0){
7210             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
7211             cs.sort(sortFn);
7212             for(var i = 0; i < len; i++){
7213                 var n = cs[i];
7214                 n.previousSibling = cs[i-1];
7215                 n.nextSibling = cs[i+1];
7216                 if(i == 0){
7217                     this.setFirstChild(n);
7218                 }
7219                 if(i == len-1){
7220                     this.setLastChild(n);
7221                 }
7222             }
7223         }
7224     },
7225
7226     /**
7227      * Returns true if this node is an ancestor (at any point) of the passed node.
7228      * @param {Node} node
7229      * @return {Boolean}
7230      */
7231     contains : function(node){
7232         return node.isAncestor(this);
7233     },
7234
7235     /**
7236      * Returns true if the passed node is an ancestor (at any point) of this node.
7237      * @param {Node} node
7238      * @return {Boolean}
7239      */
7240     isAncestor : function(node){
7241         var p = this.parentNode;
7242         while(p){
7243             if(p == node){
7244                 return true;
7245             }
7246             p = p.parentNode;
7247         }
7248         return false;
7249     },
7250
7251     toString : function(){
7252         return "[Node"+(this.id?" "+this.id:"")+"]";
7253     }
7254 });/*
7255  * Based on:
7256  * Ext JS Library 1.1.1
7257  * Copyright(c) 2006-2007, Ext JS, LLC.
7258  *
7259  * Originally Released Under LGPL - original licence link has changed is not relivant.
7260  *
7261  * Fork - LGPL
7262  * <script type="text/javascript">
7263  */
7264  
7265
7266 /**
7267  * @class Roo.ComponentMgr
7268  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
7269  * @singleton
7270  */
7271 Roo.ComponentMgr = function(){
7272     var all = new Roo.util.MixedCollection();
7273
7274     return {
7275         /**
7276          * Registers a component.
7277          * @param {Roo.Component} c The component
7278          */
7279         register : function(c){
7280             all.add(c);
7281         },
7282
7283         /**
7284          * Unregisters a component.
7285          * @param {Roo.Component} c The component
7286          */
7287         unregister : function(c){
7288             all.remove(c);
7289         },
7290
7291         /**
7292          * Returns a component by id
7293          * @param {String} id The component id
7294          */
7295         get : function(id){
7296             return all.get(id);
7297         },
7298
7299         /**
7300          * Registers a function that will be called when a specified component is added to ComponentMgr
7301          * @param {String} id The component id
7302          * @param {Funtction} fn The callback function
7303          * @param {Object} scope The scope of the callback
7304          */
7305         onAvailable : function(id, fn, scope){
7306             all.on("add", function(index, o){
7307                 if(o.id == id){
7308                     fn.call(scope || o, o);
7309                     all.un("add", fn, scope);
7310                 }
7311             });
7312         }
7313     };
7314 }();/*
7315  * Based on:
7316  * Ext JS Library 1.1.1
7317  * Copyright(c) 2006-2007, Ext JS, LLC.
7318  *
7319  * Originally Released Under LGPL - original licence link has changed is not relivant.
7320  *
7321  * Fork - LGPL
7322  * <script type="text/javascript">
7323  */
7324  
7325 /**
7326  * @class Roo.Component
7327  * @extends Roo.util.Observable
7328  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
7329  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
7330  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
7331  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
7332  * All visual components (widgets) that require rendering into a layout should subclass Component.
7333  * @constructor
7334  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
7335  * 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
7336  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
7337  */
7338 Roo.Component = function(config){
7339     config = config || {};
7340     if(config.tagName || config.dom || typeof config == "string"){ // element object
7341         config = {el: config, id: config.id || config};
7342     }
7343     this.initialConfig = config;
7344
7345     Roo.apply(this, config);
7346     this.addEvents({
7347         /**
7348          * @event disable
7349          * Fires after the component is disabled.
7350              * @param {Roo.Component} this
7351              */
7352         disable : true,
7353         /**
7354          * @event enable
7355          * Fires after the component is enabled.
7356              * @param {Roo.Component} this
7357              */
7358         enable : true,
7359         /**
7360          * @event beforeshow
7361          * Fires before the component is shown.  Return false to stop the show.
7362              * @param {Roo.Component} this
7363              */
7364         beforeshow : true,
7365         /**
7366          * @event show
7367          * Fires after the component is shown.
7368              * @param {Roo.Component} this
7369              */
7370         show : true,
7371         /**
7372          * @event beforehide
7373          * Fires before the component is hidden. Return false to stop the hide.
7374              * @param {Roo.Component} this
7375              */
7376         beforehide : true,
7377         /**
7378          * @event hide
7379          * Fires after the component is hidden.
7380              * @param {Roo.Component} this
7381              */
7382         hide : true,
7383         /**
7384          * @event beforerender
7385          * Fires before the component is rendered. Return false to stop the render.
7386              * @param {Roo.Component} this
7387              */
7388         beforerender : true,
7389         /**
7390          * @event render
7391          * Fires after the component is rendered.
7392              * @param {Roo.Component} this
7393              */
7394         render : true,
7395         /**
7396          * @event beforedestroy
7397          * Fires before the component is destroyed. Return false to stop the destroy.
7398              * @param {Roo.Component} this
7399              */
7400         beforedestroy : true,
7401         /**
7402          * @event destroy
7403          * Fires after the component is destroyed.
7404              * @param {Roo.Component} this
7405              */
7406         destroy : true
7407     });
7408     if(!this.id){
7409         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
7410     }
7411     Roo.ComponentMgr.register(this);
7412     Roo.Component.superclass.constructor.call(this);
7413     this.initComponent();
7414     if(this.renderTo){ // not supported by all components yet. use at your own risk!
7415         this.render(this.renderTo);
7416         delete this.renderTo;
7417     }
7418 };
7419
7420 /** @private */
7421 Roo.Component.AUTO_ID = 1000;
7422
7423 Roo.extend(Roo.Component, Roo.util.Observable, {
7424     /**
7425      * @scope Roo.Component.prototype
7426      * @type {Boolean}
7427      * true if this component is hidden. Read-only.
7428      */
7429     hidden : false,
7430     /**
7431      * @type {Boolean}
7432      * true if this component is disabled. Read-only.
7433      */
7434     disabled : false,
7435     /**
7436      * @type {Boolean}
7437      * true if this component has been rendered. Read-only.
7438      */
7439     rendered : false,
7440     
7441     /** @cfg {String} disableClass
7442      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
7443      */
7444     disabledClass : "x-item-disabled",
7445         /** @cfg {Boolean} allowDomMove
7446          * Whether the component can move the Dom node when rendering (defaults to true).
7447          */
7448     allowDomMove : true,
7449     /** @cfg {String} hideMode
7450      * How this component should hidden. Supported values are
7451      * "visibility" (css visibility), "offsets" (negative offset position) and
7452      * "display" (css display) - defaults to "display".
7453      */
7454     hideMode: 'display',
7455
7456     /** @private */
7457     ctype : "Roo.Component",
7458
7459     /**
7460      * @cfg {String} actionMode 
7461      * which property holds the element that used for  hide() / show() / disable() / enable()
7462      * default is 'el' 
7463      */
7464     actionMode : "el",
7465
7466     /** @private */
7467     getActionEl : function(){
7468         return this[this.actionMode];
7469     },
7470
7471     initComponent : Roo.emptyFn,
7472     /**
7473      * If this is a lazy rendering component, render it to its container element.
7474      * @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.
7475      */
7476     render : function(container, position){
7477         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
7478             if(!container && this.el){
7479                 this.el = Roo.get(this.el);
7480                 container = this.el.dom.parentNode;
7481                 this.allowDomMove = false;
7482             }
7483             this.container = Roo.get(container);
7484             this.rendered = true;
7485             if(position !== undefined){
7486                 if(typeof position == 'number'){
7487                     position = this.container.dom.childNodes[position];
7488                 }else{
7489                     position = Roo.getDom(position);
7490                 }
7491             }
7492             this.onRender(this.container, position || null);
7493             if(this.cls){
7494                 this.el.addClass(this.cls);
7495                 delete this.cls;
7496             }
7497             if(this.style){
7498                 this.el.applyStyles(this.style);
7499                 delete this.style;
7500             }
7501             this.fireEvent("render", this);
7502             this.afterRender(this.container);
7503             if(this.hidden){
7504                 this.hide();
7505             }
7506             if(this.disabled){
7507                 this.disable();
7508             }
7509         }
7510         return this;
7511     },
7512
7513     /** @private */
7514     // default function is not really useful
7515     onRender : function(ct, position){
7516         if(this.el){
7517             this.el = Roo.get(this.el);
7518             if(this.allowDomMove !== false){
7519                 ct.dom.insertBefore(this.el.dom, position);
7520             }
7521         }
7522     },
7523
7524     /** @private */
7525     getAutoCreate : function(){
7526         var cfg = typeof this.autoCreate == "object" ?
7527                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
7528         if(this.id && !cfg.id){
7529             cfg.id = this.id;
7530         }
7531         return cfg;
7532     },
7533
7534     /** @private */
7535     afterRender : Roo.emptyFn,
7536
7537     /**
7538      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
7539      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
7540      */
7541     destroy : function(){
7542         if(this.fireEvent("beforedestroy", this) !== false){
7543             this.purgeListeners();
7544             this.beforeDestroy();
7545             if(this.rendered){
7546                 this.el.removeAllListeners();
7547                 this.el.remove();
7548                 if(this.actionMode == "container"){
7549                     this.container.remove();
7550                 }
7551             }
7552             this.onDestroy();
7553             Roo.ComponentMgr.unregister(this);
7554             this.fireEvent("destroy", this);
7555         }
7556     },
7557
7558         /** @private */
7559     beforeDestroy : function(){
7560
7561     },
7562
7563         /** @private */
7564         onDestroy : function(){
7565
7566     },
7567
7568     /**
7569      * Returns the underlying {@link Roo.Element}.
7570      * @return {Roo.Element} The element
7571      */
7572     getEl : function(){
7573         return this.el;
7574     },
7575
7576     /**
7577      * Returns the id of this component.
7578      * @return {String}
7579      */
7580     getId : function(){
7581         return this.id;
7582     },
7583
7584     /**
7585      * Try to focus this component.
7586      * @param {Boolean} selectText True to also select the text in this component (if applicable)
7587      * @return {Roo.Component} this
7588      */
7589     focus : function(selectText){
7590         if(this.rendered){
7591             this.el.focus();
7592             if(selectText === true){
7593                 this.el.dom.select();
7594             }
7595         }
7596         return this;
7597     },
7598
7599     /** @private */
7600     blur : function(){
7601         if(this.rendered){
7602             this.el.blur();
7603         }
7604         return this;
7605     },
7606
7607     /**
7608      * Disable this component.
7609      * @return {Roo.Component} this
7610      */
7611     disable : function(){
7612         if(this.rendered){
7613             this.onDisable();
7614         }
7615         this.disabled = true;
7616         this.fireEvent("disable", this);
7617         return this;
7618     },
7619
7620         // private
7621     onDisable : function(){
7622         this.getActionEl().addClass(this.disabledClass);
7623         this.el.dom.disabled = true;
7624     },
7625
7626     /**
7627      * Enable this component.
7628      * @return {Roo.Component} this
7629      */
7630     enable : function(){
7631         if(this.rendered){
7632             this.onEnable();
7633         }
7634         this.disabled = false;
7635         this.fireEvent("enable", this);
7636         return this;
7637     },
7638
7639         // private
7640     onEnable : function(){
7641         this.getActionEl().removeClass(this.disabledClass);
7642         this.el.dom.disabled = false;
7643     },
7644
7645     /**
7646      * Convenience function for setting disabled/enabled by boolean.
7647      * @param {Boolean} disabled
7648      */
7649     setDisabled : function(disabled){
7650         this[disabled ? "disable" : "enable"]();
7651     },
7652
7653     /**
7654      * Show this component.
7655      * @return {Roo.Component} this
7656      */
7657     show: function(){
7658         if(this.fireEvent("beforeshow", this) !== false){
7659             this.hidden = false;
7660             if(this.rendered){
7661                 this.onShow();
7662             }
7663             this.fireEvent("show", this);
7664         }
7665         return this;
7666     },
7667
7668     // private
7669     onShow : function(){
7670         var ae = this.getActionEl();
7671         if(this.hideMode == 'visibility'){
7672             ae.dom.style.visibility = "visible";
7673         }else if(this.hideMode == 'offsets'){
7674             ae.removeClass('x-hidden');
7675         }else{
7676             ae.dom.style.display = "";
7677         }
7678     },
7679
7680     /**
7681      * Hide this component.
7682      * @return {Roo.Component} this
7683      */
7684     hide: function(){
7685         if(this.fireEvent("beforehide", this) !== false){
7686             this.hidden = true;
7687             if(this.rendered){
7688                 this.onHide();
7689             }
7690             this.fireEvent("hide", this);
7691         }
7692         return this;
7693     },
7694
7695     // private
7696     onHide : function(){
7697         var ae = this.getActionEl();
7698         if(this.hideMode == 'visibility'){
7699             ae.dom.style.visibility = "hidden";
7700         }else if(this.hideMode == 'offsets'){
7701             ae.addClass('x-hidden');
7702         }else{
7703             ae.dom.style.display = "none";
7704         }
7705     },
7706
7707     /**
7708      * Convenience function to hide or show this component by boolean.
7709      * @param {Boolean} visible True to show, false to hide
7710      * @return {Roo.Component} this
7711      */
7712     setVisible: function(visible){
7713         if(visible) {
7714             this.show();
7715         }else{
7716             this.hide();
7717         }
7718         return this;
7719     },
7720
7721     /**
7722      * Returns true if this component is visible.
7723      */
7724     isVisible : function(){
7725         return this.getActionEl().isVisible();
7726     },
7727
7728     cloneConfig : function(overrides){
7729         overrides = overrides || {};
7730         var id = overrides.id || Roo.id();
7731         var cfg = Roo.applyIf(overrides, this.initialConfig);
7732         cfg.id = id; // prevent dup id
7733         return new this.constructor(cfg);
7734     }
7735 });/*
7736  * Based on:
7737  * Ext JS Library 1.1.1
7738  * Copyright(c) 2006-2007, Ext JS, LLC.
7739  *
7740  * Originally Released Under LGPL - original licence link has changed is not relivant.
7741  *
7742  * Fork - LGPL
7743  * <script type="text/javascript">
7744  */
7745  (function(){ 
7746 /**
7747  * @class Roo.Layer
7748  * @extends Roo.Element
7749  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
7750  * automatic maintaining of shadow/shim positions.
7751  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
7752  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
7753  * you can pass a string with a CSS class name. False turns off the shadow.
7754  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
7755  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
7756  * @cfg {String} cls CSS class to add to the element
7757  * @cfg {Number} zindex Starting z-index (defaults to 11000)
7758  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
7759  * @constructor
7760  * @param {Object} config An object with config options.
7761  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
7762  */
7763
7764 Roo.Layer = function(config, existingEl){
7765     config = config || {};
7766     var dh = Roo.DomHelper;
7767     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
7768     if(existingEl){
7769         this.dom = Roo.getDom(existingEl);
7770     }
7771     if(!this.dom){
7772         var o = config.dh || {tag: "div", cls: "x-layer"};
7773         this.dom = dh.append(pel, o);
7774     }
7775     if(config.cls){
7776         this.addClass(config.cls);
7777     }
7778     this.constrain = config.constrain !== false;
7779     this.visibilityMode = Roo.Element.VISIBILITY;
7780     if(config.id){
7781         this.id = this.dom.id = config.id;
7782     }else{
7783         this.id = Roo.id(this.dom);
7784     }
7785     this.zindex = config.zindex || this.getZIndex();
7786     this.position("absolute", this.zindex);
7787     if(config.shadow){
7788         this.shadowOffset = config.shadowOffset || 4;
7789         this.shadow = new Roo.Shadow({
7790             offset : this.shadowOffset,
7791             mode : config.shadow
7792         });
7793     }else{
7794         this.shadowOffset = 0;
7795     }
7796     this.useShim = config.shim !== false && Roo.useShims;
7797     this.useDisplay = config.useDisplay;
7798     this.hide();
7799 };
7800
7801 var supr = Roo.Element.prototype;
7802
7803 // shims are shared among layer to keep from having 100 iframes
7804 var shims = [];
7805
7806 Roo.extend(Roo.Layer, Roo.Element, {
7807
7808     getZIndex : function(){
7809         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
7810     },
7811
7812     getShim : function(){
7813         if(!this.useShim){
7814             return null;
7815         }
7816         if(this.shim){
7817             return this.shim;
7818         }
7819         var shim = shims.shift();
7820         if(!shim){
7821             shim = this.createShim();
7822             shim.enableDisplayMode('block');
7823             shim.dom.style.display = 'none';
7824             shim.dom.style.visibility = 'visible';
7825         }
7826         var pn = this.dom.parentNode;
7827         if(shim.dom.parentNode != pn){
7828             pn.insertBefore(shim.dom, this.dom);
7829         }
7830         shim.setStyle('z-index', this.getZIndex()-2);
7831         this.shim = shim;
7832         return shim;
7833     },
7834
7835     hideShim : function(){
7836         if(this.shim){
7837             this.shim.setDisplayed(false);
7838             shims.push(this.shim);
7839             delete this.shim;
7840         }
7841     },
7842
7843     disableShadow : function(){
7844         if(this.shadow){
7845             this.shadowDisabled = true;
7846             this.shadow.hide();
7847             this.lastShadowOffset = this.shadowOffset;
7848             this.shadowOffset = 0;
7849         }
7850     },
7851
7852     enableShadow : function(show){
7853         if(this.shadow){
7854             this.shadowDisabled = false;
7855             this.shadowOffset = this.lastShadowOffset;
7856             delete this.lastShadowOffset;
7857             if(show){
7858                 this.sync(true);
7859             }
7860         }
7861     },
7862
7863     // private
7864     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
7865     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
7866     sync : function(doShow){
7867         var sw = this.shadow;
7868         if(!this.updating && this.isVisible() && (sw || this.useShim)){
7869             var sh = this.getShim();
7870
7871             var w = this.getWidth(),
7872                 h = this.getHeight();
7873
7874             var l = this.getLeft(true),
7875                 t = this.getTop(true);
7876
7877             if(sw && !this.shadowDisabled){
7878                 if(doShow && !sw.isVisible()){
7879                     sw.show(this);
7880                 }else{
7881                     sw.realign(l, t, w, h);
7882                 }
7883                 if(sh){
7884                     if(doShow){
7885                        sh.show();
7886                     }
7887                     // fit the shim behind the shadow, so it is shimmed too
7888                     var a = sw.adjusts, s = sh.dom.style;
7889                     s.left = (Math.min(l, l+a.l))+"px";
7890                     s.top = (Math.min(t, t+a.t))+"px";
7891                     s.width = (w+a.w)+"px";
7892                     s.height = (h+a.h)+"px";
7893                 }
7894             }else if(sh){
7895                 if(doShow){
7896                    sh.show();
7897                 }
7898                 sh.setSize(w, h);
7899                 sh.setLeftTop(l, t);
7900             }
7901             
7902         }
7903     },
7904
7905     // private
7906     destroy : function(){
7907         this.hideShim();
7908         if(this.shadow){
7909             this.shadow.hide();
7910         }
7911         this.removeAllListeners();
7912         var pn = this.dom.parentNode;
7913         if(pn){
7914             pn.removeChild(this.dom);
7915         }
7916         Roo.Element.uncache(this.id);
7917     },
7918
7919     remove : function(){
7920         this.destroy();
7921     },
7922
7923     // private
7924     beginUpdate : function(){
7925         this.updating = true;
7926     },
7927
7928     // private
7929     endUpdate : function(){
7930         this.updating = false;
7931         this.sync(true);
7932     },
7933
7934     // private
7935     hideUnders : function(negOffset){
7936         if(this.shadow){
7937             this.shadow.hide();
7938         }
7939         this.hideShim();
7940     },
7941
7942     // private
7943     constrainXY : function(){
7944         if(this.constrain){
7945             var vw = Roo.lib.Dom.getViewWidth(),
7946                 vh = Roo.lib.Dom.getViewHeight();
7947             var s = Roo.get(document).getScroll();
7948
7949             var xy = this.getXY();
7950             var x = xy[0], y = xy[1];   
7951             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
7952             // only move it if it needs it
7953             var moved = false;
7954             // first validate right/bottom
7955             if((x + w) > vw+s.left){
7956                 x = vw - w - this.shadowOffset;
7957                 moved = true;
7958             }
7959             if((y + h) > vh+s.top){
7960                 y = vh - h - this.shadowOffset;
7961                 moved = true;
7962             }
7963             // then make sure top/left isn't negative
7964             if(x < s.left){
7965                 x = s.left;
7966                 moved = true;
7967             }
7968             if(y < s.top){
7969                 y = s.top;
7970                 moved = true;
7971             }
7972             if(moved){
7973                 if(this.avoidY){
7974                     var ay = this.avoidY;
7975                     if(y <= ay && (y+h) >= ay){
7976                         y = ay-h-5;   
7977                     }
7978                 }
7979                 xy = [x, y];
7980                 this.storeXY(xy);
7981                 supr.setXY.call(this, xy);
7982                 this.sync();
7983             }
7984         }
7985     },
7986
7987     isVisible : function(){
7988         return this.visible;    
7989     },
7990
7991     // private
7992     showAction : function(){
7993         this.visible = true; // track visibility to prevent getStyle calls
7994         if(this.useDisplay === true){
7995             this.setDisplayed("");
7996         }else if(this.lastXY){
7997             supr.setXY.call(this, this.lastXY);
7998         }else if(this.lastLT){
7999             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
8000         }
8001     },
8002
8003     // private
8004     hideAction : function(){
8005         this.visible = false;
8006         if(this.useDisplay === true){
8007             this.setDisplayed(false);
8008         }else{
8009             this.setLeftTop(-10000,-10000);
8010         }
8011     },
8012
8013     // overridden Element method
8014     setVisible : function(v, a, d, c, e){
8015         if(v){
8016             this.showAction();
8017         }
8018         if(a && v){
8019             var cb = function(){
8020                 this.sync(true);
8021                 if(c){
8022                     c();
8023                 }
8024             }.createDelegate(this);
8025             supr.setVisible.call(this, true, true, d, cb, e);
8026         }else{
8027             if(!v){
8028                 this.hideUnders(true);
8029             }
8030             var cb = c;
8031             if(a){
8032                 cb = function(){
8033                     this.hideAction();
8034                     if(c){
8035                         c();
8036                     }
8037                 }.createDelegate(this);
8038             }
8039             supr.setVisible.call(this, v, a, d, cb, e);
8040             if(v){
8041                 this.sync(true);
8042             }else if(!a){
8043                 this.hideAction();
8044             }
8045         }
8046     },
8047
8048     storeXY : function(xy){
8049         delete this.lastLT;
8050         this.lastXY = xy;
8051     },
8052
8053     storeLeftTop : function(left, top){
8054         delete this.lastXY;
8055         this.lastLT = [left, top];
8056     },
8057
8058     // private
8059     beforeFx : function(){
8060         this.beforeAction();
8061         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
8062     },
8063
8064     // private
8065     afterFx : function(){
8066         Roo.Layer.superclass.afterFx.apply(this, arguments);
8067         this.sync(this.isVisible());
8068     },
8069
8070     // private
8071     beforeAction : function(){
8072         if(!this.updating && this.shadow){
8073             this.shadow.hide();
8074         }
8075     },
8076
8077     // overridden Element method
8078     setLeft : function(left){
8079         this.storeLeftTop(left, this.getTop(true));
8080         supr.setLeft.apply(this, arguments);
8081         this.sync();
8082     },
8083
8084     setTop : function(top){
8085         this.storeLeftTop(this.getLeft(true), top);
8086         supr.setTop.apply(this, arguments);
8087         this.sync();
8088     },
8089
8090     setLeftTop : function(left, top){
8091         this.storeLeftTop(left, top);
8092         supr.setLeftTop.apply(this, arguments);
8093         this.sync();
8094     },
8095
8096     setXY : function(xy, a, d, c, e){
8097         this.fixDisplay();
8098         this.beforeAction();
8099         this.storeXY(xy);
8100         var cb = this.createCB(c);
8101         supr.setXY.call(this, xy, a, d, cb, e);
8102         if(!a){
8103             cb();
8104         }
8105     },
8106
8107     // private
8108     createCB : function(c){
8109         var el = this;
8110         return function(){
8111             el.constrainXY();
8112             el.sync(true);
8113             if(c){
8114                 c();
8115             }
8116         };
8117     },
8118
8119     // overridden Element method
8120     setX : function(x, a, d, c, e){
8121         this.setXY([x, this.getY()], a, d, c, e);
8122     },
8123
8124     // overridden Element method
8125     setY : function(y, a, d, c, e){
8126         this.setXY([this.getX(), y], a, d, c, e);
8127     },
8128
8129     // overridden Element method
8130     setSize : function(w, h, a, d, c, e){
8131         this.beforeAction();
8132         var cb = this.createCB(c);
8133         supr.setSize.call(this, w, h, a, d, cb, e);
8134         if(!a){
8135             cb();
8136         }
8137     },
8138
8139     // overridden Element method
8140     setWidth : function(w, a, d, c, e){
8141         this.beforeAction();
8142         var cb = this.createCB(c);
8143         supr.setWidth.call(this, w, a, d, cb, e);
8144         if(!a){
8145             cb();
8146         }
8147     },
8148
8149     // overridden Element method
8150     setHeight : function(h, a, d, c, e){
8151         this.beforeAction();
8152         var cb = this.createCB(c);
8153         supr.setHeight.call(this, h, a, d, cb, e);
8154         if(!a){
8155             cb();
8156         }
8157     },
8158
8159     // overridden Element method
8160     setBounds : function(x, y, w, h, a, d, c, e){
8161         this.beforeAction();
8162         var cb = this.createCB(c);
8163         if(!a){
8164             this.storeXY([x, y]);
8165             supr.setXY.call(this, [x, y]);
8166             supr.setSize.call(this, w, h, a, d, cb, e);
8167             cb();
8168         }else{
8169             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
8170         }
8171         return this;
8172     },
8173     
8174     /**
8175      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
8176      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
8177      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
8178      * @param {Number} zindex The new z-index to set
8179      * @return {this} The Layer
8180      */
8181     setZIndex : function(zindex){
8182         this.zindex = zindex;
8183         this.setStyle("z-index", zindex + 2);
8184         if(this.shadow){
8185             this.shadow.setZIndex(zindex + 1);
8186         }
8187         if(this.shim){
8188             this.shim.setStyle("z-index", zindex);
8189         }
8190     }
8191 });
8192 })();/*
8193  * Based on:
8194  * Ext JS Library 1.1.1
8195  * Copyright(c) 2006-2007, Ext JS, LLC.
8196  *
8197  * Originally Released Under LGPL - original licence link has changed is not relivant.
8198  *
8199  * Fork - LGPL
8200  * <script type="text/javascript">
8201  */
8202
8203
8204 /**
8205  * @class Roo.Shadow
8206  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
8207  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
8208  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
8209  * @constructor
8210  * Create a new Shadow
8211  * @param {Object} config The config object
8212  */
8213 Roo.Shadow = function(config){
8214     Roo.apply(this, config);
8215     if(typeof this.mode != "string"){
8216         this.mode = this.defaultMode;
8217     }
8218     var o = this.offset, a = {h: 0};
8219     var rad = Math.floor(this.offset/2);
8220     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
8221         case "drop":
8222             a.w = 0;
8223             a.l = a.t = o;
8224             a.t -= 1;
8225             if(Roo.isIE){
8226                 a.l -= this.offset + rad;
8227                 a.t -= this.offset + rad;
8228                 a.w -= rad;
8229                 a.h -= rad;
8230                 a.t += 1;
8231             }
8232         break;
8233         case "sides":
8234             a.w = (o*2);
8235             a.l = -o;
8236             a.t = o-1;
8237             if(Roo.isIE){
8238                 a.l -= (this.offset - rad);
8239                 a.t -= this.offset + rad;
8240                 a.l += 1;
8241                 a.w -= (this.offset - rad)*2;
8242                 a.w -= rad + 1;
8243                 a.h -= 1;
8244             }
8245         break;
8246         case "frame":
8247             a.w = a.h = (o*2);
8248             a.l = a.t = -o;
8249             a.t += 1;
8250             a.h -= 2;
8251             if(Roo.isIE){
8252                 a.l -= (this.offset - rad);
8253                 a.t -= (this.offset - rad);
8254                 a.l += 1;
8255                 a.w -= (this.offset + rad + 1);
8256                 a.h -= (this.offset + rad);
8257                 a.h += 1;
8258             }
8259         break;
8260     };
8261
8262     this.adjusts = a;
8263 };
8264
8265 Roo.Shadow.prototype = {
8266     /**
8267      * @cfg {String} mode
8268      * The shadow display mode.  Supports the following options:<br />
8269      * sides: Shadow displays on both sides and bottom only<br />
8270      * frame: Shadow displays equally on all four sides<br />
8271      * drop: Traditional bottom-right drop shadow (default)
8272      */
8273     /**
8274      * @cfg {String} offset
8275      * The number of pixels to offset the shadow from the element (defaults to 4)
8276      */
8277     offset: 4,
8278
8279     // private
8280     defaultMode: "drop",
8281
8282     /**
8283      * Displays the shadow under the target element
8284      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
8285      */
8286     show : function(target){
8287         target = Roo.get(target);
8288         if(!this.el){
8289             this.el = Roo.Shadow.Pool.pull();
8290             if(this.el.dom.nextSibling != target.dom){
8291                 this.el.insertBefore(target);
8292             }
8293         }
8294         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
8295         if(Roo.isIE){
8296             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
8297         }
8298         this.realign(
8299             target.getLeft(true),
8300             target.getTop(true),
8301             target.getWidth(),
8302             target.getHeight()
8303         );
8304         this.el.dom.style.display = "block";
8305     },
8306
8307     /**
8308      * Returns true if the shadow is visible, else false
8309      */
8310     isVisible : function(){
8311         return this.el ? true : false;  
8312     },
8313
8314     /**
8315      * Direct alignment when values are already available. Show must be called at least once before
8316      * calling this method to ensure it is initialized.
8317      * @param {Number} left The target element left position
8318      * @param {Number} top The target element top position
8319      * @param {Number} width The target element width
8320      * @param {Number} height The target element height
8321      */
8322     realign : function(l, t, w, h){
8323         if(!this.el){
8324             return;
8325         }
8326         var a = this.adjusts, d = this.el.dom, s = d.style;
8327         var iea = 0;
8328         s.left = (l+a.l)+"px";
8329         s.top = (t+a.t)+"px";
8330         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
8331  
8332         if(s.width != sws || s.height != shs){
8333             s.width = sws;
8334             s.height = shs;
8335             if(!Roo.isIE){
8336                 var cn = d.childNodes;
8337                 var sww = Math.max(0, (sw-12))+"px";
8338                 cn[0].childNodes[1].style.width = sww;
8339                 cn[1].childNodes[1].style.width = sww;
8340                 cn[2].childNodes[1].style.width = sww;
8341                 cn[1].style.height = Math.max(0, (sh-12))+"px";
8342             }
8343         }
8344     },
8345
8346     /**
8347      * Hides this shadow
8348      */
8349     hide : function(){
8350         if(this.el){
8351             this.el.dom.style.display = "none";
8352             Roo.Shadow.Pool.push(this.el);
8353             delete this.el;
8354         }
8355     },
8356
8357     /**
8358      * Adjust the z-index of this shadow
8359      * @param {Number} zindex The new z-index
8360      */
8361     setZIndex : function(z){
8362         this.zIndex = z;
8363         if(this.el){
8364             this.el.setStyle("z-index", z);
8365         }
8366     }
8367 };
8368
8369 // Private utility class that manages the internal Shadow cache
8370 Roo.Shadow.Pool = function(){
8371     var p = [];
8372     var markup = Roo.isIE ?
8373                  '<div class="x-ie-shadow"></div>' :
8374                  '<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>';
8375     return {
8376         pull : function(){
8377             var sh = p.shift();
8378             if(!sh){
8379                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
8380                 sh.autoBoxAdjust = false;
8381             }
8382             return sh;
8383         },
8384
8385         push : function(sh){
8386             p.push(sh);
8387         }
8388     };
8389 }();/*
8390  * Based on:
8391  * Ext JS Library 1.1.1
8392  * Copyright(c) 2006-2007, Ext JS, LLC.
8393  *
8394  * Originally Released Under LGPL - original licence link has changed is not relivant.
8395  *
8396  * Fork - LGPL
8397  * <script type="text/javascript">
8398  */
8399
8400 /**
8401  * @class Roo.BoxComponent
8402  * @extends Roo.Component
8403  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
8404  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
8405  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
8406  * layout containers.
8407  * @constructor
8408  * @param {Roo.Element/String/Object} config The configuration options.
8409  */
8410 Roo.BoxComponent = function(config){
8411     Roo.Component.call(this, config);
8412     this.addEvents({
8413         /**
8414          * @event resize
8415          * Fires after the component is resized.
8416              * @param {Roo.Component} this
8417              * @param {Number} adjWidth The box-adjusted width that was set
8418              * @param {Number} adjHeight The box-adjusted height that was set
8419              * @param {Number} rawWidth The width that was originally specified
8420              * @param {Number} rawHeight The height that was originally specified
8421              */
8422         resize : true,
8423         /**
8424          * @event move
8425          * Fires after the component is moved.
8426              * @param {Roo.Component} this
8427              * @param {Number} x The new x position
8428              * @param {Number} y The new y position
8429              */
8430         move : true
8431     });
8432 };
8433
8434 Roo.extend(Roo.BoxComponent, Roo.Component, {
8435     // private, set in afterRender to signify that the component has been rendered
8436     boxReady : false,
8437     // private, used to defer height settings to subclasses
8438     deferHeight: false,
8439     /** @cfg {Number} width
8440      * width (optional) size of component
8441      */
8442      /** @cfg {Number} height
8443      * height (optional) size of component
8444      */
8445      
8446     /**
8447      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
8448      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
8449      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
8450      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
8451      * @return {Roo.BoxComponent} this
8452      */
8453     setSize : function(w, h){
8454         // support for standard size objects
8455         if(typeof w == 'object'){
8456             h = w.height;
8457             w = w.width;
8458         }
8459         // not rendered
8460         if(!this.boxReady){
8461             this.width = w;
8462             this.height = h;
8463             return this;
8464         }
8465
8466         // prevent recalcs when not needed
8467         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
8468             return this;
8469         }
8470         this.lastSize = {width: w, height: h};
8471
8472         var adj = this.adjustSize(w, h);
8473         var aw = adj.width, ah = adj.height;
8474         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
8475             var rz = this.getResizeEl();
8476             if(!this.deferHeight && aw !== undefined && ah !== undefined){
8477                 rz.setSize(aw, ah);
8478             }else if(!this.deferHeight && ah !== undefined){
8479                 rz.setHeight(ah);
8480             }else if(aw !== undefined){
8481                 rz.setWidth(aw);
8482             }
8483             this.onResize(aw, ah, w, h);
8484             this.fireEvent('resize', this, aw, ah, w, h);
8485         }
8486         return this;
8487     },
8488
8489     /**
8490      * Gets the current size of the component's underlying element.
8491      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8492      */
8493     getSize : function(){
8494         return this.el.getSize();
8495     },
8496
8497     /**
8498      * Gets the current XY position of the component's underlying element.
8499      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8500      * @return {Array} The XY position of the element (e.g., [100, 200])
8501      */
8502     getPosition : function(local){
8503         if(local === true){
8504             return [this.el.getLeft(true), this.el.getTop(true)];
8505         }
8506         return this.xy || this.el.getXY();
8507     },
8508
8509     /**
8510      * Gets the current box measurements of the component's underlying element.
8511      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8512      * @returns {Object} box An object in the format {x, y, width, height}
8513      */
8514     getBox : function(local){
8515         var s = this.el.getSize();
8516         if(local){
8517             s.x = this.el.getLeft(true);
8518             s.y = this.el.getTop(true);
8519         }else{
8520             var xy = this.xy || this.el.getXY();
8521             s.x = xy[0];
8522             s.y = xy[1];
8523         }
8524         return s;
8525     },
8526
8527     /**
8528      * Sets the current box measurements of the component's underlying element.
8529      * @param {Object} box An object in the format {x, y, width, height}
8530      * @returns {Roo.BoxComponent} this
8531      */
8532     updateBox : function(box){
8533         this.setSize(box.width, box.height);
8534         this.setPagePosition(box.x, box.y);
8535         return this;
8536     },
8537
8538     // protected
8539     getResizeEl : function(){
8540         return this.resizeEl || this.el;
8541     },
8542
8543     // protected
8544     getPositionEl : function(){
8545         return this.positionEl || this.el;
8546     },
8547
8548     /**
8549      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
8550      * This method fires the move event.
8551      * @param {Number} left The new left
8552      * @param {Number} top The new top
8553      * @returns {Roo.BoxComponent} this
8554      */
8555     setPosition : function(x, y){
8556         this.x = x;
8557         this.y = y;
8558         if(!this.boxReady){
8559             return this;
8560         }
8561         var adj = this.adjustPosition(x, y);
8562         var ax = adj.x, ay = adj.y;
8563
8564         var el = this.getPositionEl();
8565         if(ax !== undefined || ay !== undefined){
8566             if(ax !== undefined && ay !== undefined){
8567                 el.setLeftTop(ax, ay);
8568             }else if(ax !== undefined){
8569                 el.setLeft(ax);
8570             }else if(ay !== undefined){
8571                 el.setTop(ay);
8572             }
8573             this.onPosition(ax, ay);
8574             this.fireEvent('move', this, ax, ay);
8575         }
8576         return this;
8577     },
8578
8579     /**
8580      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
8581      * This method fires the move event.
8582      * @param {Number} x The new x position
8583      * @param {Number} y The new y position
8584      * @returns {Roo.BoxComponent} this
8585      */
8586     setPagePosition : function(x, y){
8587         this.pageX = x;
8588         this.pageY = y;
8589         if(!this.boxReady){
8590             return;
8591         }
8592         if(x === undefined || y === undefined){ // cannot translate undefined points
8593             return;
8594         }
8595         var p = this.el.translatePoints(x, y);
8596         this.setPosition(p.left, p.top);
8597         return this;
8598     },
8599
8600     // private
8601     onRender : function(ct, position){
8602         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
8603         if(this.resizeEl){
8604             this.resizeEl = Roo.get(this.resizeEl);
8605         }
8606         if(this.positionEl){
8607             this.positionEl = Roo.get(this.positionEl);
8608         }
8609     },
8610
8611     // private
8612     afterRender : function(){
8613         Roo.BoxComponent.superclass.afterRender.call(this);
8614         this.boxReady = true;
8615         this.setSize(this.width, this.height);
8616         if(this.x || this.y){
8617             this.setPosition(this.x, this.y);
8618         }
8619         if(this.pageX || this.pageY){
8620             this.setPagePosition(this.pageX, this.pageY);
8621         }
8622     },
8623
8624     /**
8625      * Force the component's size to recalculate based on the underlying element's current height and width.
8626      * @returns {Roo.BoxComponent} this
8627      */
8628     syncSize : function(){
8629         delete this.lastSize;
8630         this.setSize(this.el.getWidth(), this.el.getHeight());
8631         return this;
8632     },
8633
8634     /**
8635      * Called after the component is resized, this method is empty by default but can be implemented by any
8636      * subclass that needs to perform custom logic after a resize occurs.
8637      * @param {Number} adjWidth The box-adjusted width that was set
8638      * @param {Number} adjHeight The box-adjusted height that was set
8639      * @param {Number} rawWidth The width that was originally specified
8640      * @param {Number} rawHeight The height that was originally specified
8641      */
8642     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
8643
8644     },
8645
8646     /**
8647      * Called after the component is moved, this method is empty by default but can be implemented by any
8648      * subclass that needs to perform custom logic after a move occurs.
8649      * @param {Number} x The new x position
8650      * @param {Number} y The new y position
8651      */
8652     onPosition : function(x, y){
8653
8654     },
8655
8656     // private
8657     adjustSize : function(w, h){
8658         if(this.autoWidth){
8659             w = 'auto';
8660         }
8661         if(this.autoHeight){
8662             h = 'auto';
8663         }
8664         return {width : w, height: h};
8665     },
8666
8667     // private
8668     adjustPosition : function(x, y){
8669         return {x : x, y: y};
8670     }
8671 });/*
8672  * Based on:
8673  * Ext JS Library 1.1.1
8674  * Copyright(c) 2006-2007, Ext JS, LLC.
8675  *
8676  * Originally Released Under LGPL - original licence link has changed is not relivant.
8677  *
8678  * Fork - LGPL
8679  * <script type="text/javascript">
8680  */
8681
8682
8683 /**
8684  * @class Roo.SplitBar
8685  * @extends Roo.util.Observable
8686  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
8687  * <br><br>
8688  * Usage:
8689  * <pre><code>
8690 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
8691                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
8692 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
8693 split.minSize = 100;
8694 split.maxSize = 600;
8695 split.animate = true;
8696 split.on('moved', splitterMoved);
8697 </code></pre>
8698  * @constructor
8699  * Create a new SplitBar
8700  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
8701  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
8702  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8703  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
8704                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
8705                         position of the SplitBar).
8706  */
8707 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
8708     
8709     /** @private */
8710     this.el = Roo.get(dragElement, true);
8711     this.el.dom.unselectable = "on";
8712     /** @private */
8713     this.resizingEl = Roo.get(resizingElement, true);
8714
8715     /**
8716      * @private
8717      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8718      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
8719      * @type Number
8720      */
8721     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
8722     
8723     /**
8724      * The minimum size of the resizing element. (Defaults to 0)
8725      * @type Number
8726      */
8727     this.minSize = 0;
8728     
8729     /**
8730      * The maximum size of the resizing element. (Defaults to 2000)
8731      * @type Number
8732      */
8733     this.maxSize = 2000;
8734     
8735     /**
8736      * Whether to animate the transition to the new size
8737      * @type Boolean
8738      */
8739     this.animate = false;
8740     
8741     /**
8742      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
8743      * @type Boolean
8744      */
8745     this.useShim = false;
8746     
8747     /** @private */
8748     this.shim = null;
8749     
8750     if(!existingProxy){
8751         /** @private */
8752         this.proxy = Roo.SplitBar.createProxy(this.orientation);
8753     }else{
8754         this.proxy = Roo.get(existingProxy).dom;
8755     }
8756     /** @private */
8757     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
8758     
8759     /** @private */
8760     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
8761     
8762     /** @private */
8763     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
8764     
8765     /** @private */
8766     this.dragSpecs = {};
8767     
8768     /**
8769      * @private The adapter to use to positon and resize elements
8770      */
8771     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
8772     this.adapter.init(this);
8773     
8774     if(this.orientation == Roo.SplitBar.HORIZONTAL){
8775         /** @private */
8776         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
8777         this.el.addClass("x-splitbar-h");
8778     }else{
8779         /** @private */
8780         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
8781         this.el.addClass("x-splitbar-v");
8782     }
8783     
8784     this.addEvents({
8785         /**
8786          * @event resize
8787          * Fires when the splitter is moved (alias for {@link #event-moved})
8788          * @param {Roo.SplitBar} this
8789          * @param {Number} newSize the new width or height
8790          */
8791         "resize" : true,
8792         /**
8793          * @event moved
8794          * Fires when the splitter is moved
8795          * @param {Roo.SplitBar} this
8796          * @param {Number} newSize the new width or height
8797          */
8798         "moved" : true,
8799         /**
8800          * @event beforeresize
8801          * Fires before the splitter is dragged
8802          * @param {Roo.SplitBar} this
8803          */
8804         "beforeresize" : true,
8805
8806         "beforeapply" : true
8807     });
8808
8809     Roo.util.Observable.call(this);
8810 };
8811
8812 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
8813     onStartProxyDrag : function(x, y){
8814         this.fireEvent("beforeresize", this);
8815         if(!this.overlay){
8816             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
8817             o.unselectable();
8818             o.enableDisplayMode("block");
8819             // all splitbars share the same overlay
8820             Roo.SplitBar.prototype.overlay = o;
8821         }
8822         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8823         this.overlay.show();
8824         Roo.get(this.proxy).setDisplayed("block");
8825         var size = this.adapter.getElementSize(this);
8826         this.activeMinSize = this.getMinimumSize();;
8827         this.activeMaxSize = this.getMaximumSize();;
8828         var c1 = size - this.activeMinSize;
8829         var c2 = Math.max(this.activeMaxSize - size, 0);
8830         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8831             this.dd.resetConstraints();
8832             this.dd.setXConstraint(
8833                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
8834                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
8835             );
8836             this.dd.setYConstraint(0, 0);
8837         }else{
8838             this.dd.resetConstraints();
8839             this.dd.setXConstraint(0, 0);
8840             this.dd.setYConstraint(
8841                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
8842                 this.placement == Roo.SplitBar.TOP ? c2 : c1
8843             );
8844          }
8845         this.dragSpecs.startSize = size;
8846         this.dragSpecs.startPoint = [x, y];
8847         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
8848     },
8849     
8850     /** 
8851      * @private Called after the drag operation by the DDProxy
8852      */
8853     onEndProxyDrag : function(e){
8854         Roo.get(this.proxy).setDisplayed(false);
8855         var endPoint = Roo.lib.Event.getXY(e);
8856         if(this.overlay){
8857             this.overlay.hide();
8858         }
8859         var newSize;
8860         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8861             newSize = this.dragSpecs.startSize + 
8862                 (this.placement == Roo.SplitBar.LEFT ?
8863                     endPoint[0] - this.dragSpecs.startPoint[0] :
8864                     this.dragSpecs.startPoint[0] - endPoint[0]
8865                 );
8866         }else{
8867             newSize = this.dragSpecs.startSize + 
8868                 (this.placement == Roo.SplitBar.TOP ?
8869                     endPoint[1] - this.dragSpecs.startPoint[1] :
8870                     this.dragSpecs.startPoint[1] - endPoint[1]
8871                 );
8872         }
8873         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
8874         if(newSize != this.dragSpecs.startSize){
8875             if(this.fireEvent('beforeapply', this, newSize) !== false){
8876                 this.adapter.setElementSize(this, newSize);
8877                 this.fireEvent("moved", this, newSize);
8878                 this.fireEvent("resize", this, newSize);
8879             }
8880         }
8881     },
8882     
8883     /**
8884      * Get the adapter this SplitBar uses
8885      * @return The adapter object
8886      */
8887     getAdapter : function(){
8888         return this.adapter;
8889     },
8890     
8891     /**
8892      * Set the adapter this SplitBar uses
8893      * @param {Object} adapter A SplitBar adapter object
8894      */
8895     setAdapter : function(adapter){
8896         this.adapter = adapter;
8897         this.adapter.init(this);
8898     },
8899     
8900     /**
8901      * Gets the minimum size for the resizing element
8902      * @return {Number} The minimum size
8903      */
8904     getMinimumSize : function(){
8905         return this.minSize;
8906     },
8907     
8908     /**
8909      * Sets the minimum size for the resizing element
8910      * @param {Number} minSize The minimum size
8911      */
8912     setMinimumSize : function(minSize){
8913         this.minSize = minSize;
8914     },
8915     
8916     /**
8917      * Gets the maximum size for the resizing element
8918      * @return {Number} The maximum size
8919      */
8920     getMaximumSize : function(){
8921         return this.maxSize;
8922     },
8923     
8924     /**
8925      * Sets the maximum size for the resizing element
8926      * @param {Number} maxSize The maximum size
8927      */
8928     setMaximumSize : function(maxSize){
8929         this.maxSize = maxSize;
8930     },
8931     
8932     /**
8933      * Sets the initialize size for the resizing element
8934      * @param {Number} size The initial size
8935      */
8936     setCurrentSize : function(size){
8937         var oldAnimate = this.animate;
8938         this.animate = false;
8939         this.adapter.setElementSize(this, size);
8940         this.animate = oldAnimate;
8941     },
8942     
8943     /**
8944      * Destroy this splitbar. 
8945      * @param {Boolean} removeEl True to remove the element
8946      */
8947     destroy : function(removeEl){
8948         if(this.shim){
8949             this.shim.remove();
8950         }
8951         this.dd.unreg();
8952         this.proxy.parentNode.removeChild(this.proxy);
8953         if(removeEl){
8954             this.el.remove();
8955         }
8956     }
8957 });
8958
8959 /**
8960  * @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.
8961  */
8962 Roo.SplitBar.createProxy = function(dir){
8963     var proxy = new Roo.Element(document.createElement("div"));
8964     proxy.unselectable();
8965     var cls = 'x-splitbar-proxy';
8966     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
8967     document.body.appendChild(proxy.dom);
8968     return proxy.dom;
8969 };
8970
8971 /** 
8972  * @class Roo.SplitBar.BasicLayoutAdapter
8973  * Default Adapter. It assumes the splitter and resizing element are not positioned
8974  * elements and only gets/sets the width of the element. Generally used for table based layouts.
8975  */
8976 Roo.SplitBar.BasicLayoutAdapter = function(){
8977 };
8978
8979 Roo.SplitBar.BasicLayoutAdapter.prototype = {
8980     // do nothing for now
8981     init : function(s){
8982     
8983     },
8984     /**
8985      * Called before drag operations to get the current size of the resizing element. 
8986      * @param {Roo.SplitBar} s The SplitBar using this adapter
8987      */
8988      getElementSize : function(s){
8989         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8990             return s.resizingEl.getWidth();
8991         }else{
8992             return s.resizingEl.getHeight();
8993         }
8994     },
8995     
8996     /**
8997      * Called after drag operations to set the size of the resizing element.
8998      * @param {Roo.SplitBar} s The SplitBar using this adapter
8999      * @param {Number} newSize The new size to set
9000      * @param {Function} onComplete A function to be invoked when resizing is complete
9001      */
9002     setElementSize : function(s, newSize, onComplete){
9003         if(s.orientation == Roo.SplitBar.HORIZONTAL){
9004             if(!s.animate){
9005                 s.resizingEl.setWidth(newSize);
9006                 if(onComplete){
9007                     onComplete(s, newSize);
9008                 }
9009             }else{
9010                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
9011             }
9012         }else{
9013             
9014             if(!s.animate){
9015                 s.resizingEl.setHeight(newSize);
9016                 if(onComplete){
9017                     onComplete(s, newSize);
9018                 }
9019             }else{
9020                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
9021             }
9022         }
9023     }
9024 };
9025
9026 /** 
9027  *@class Roo.SplitBar.AbsoluteLayoutAdapter
9028  * @extends Roo.SplitBar.BasicLayoutAdapter
9029  * Adapter that  moves the splitter element to align with the resized sizing element. 
9030  * Used with an absolute positioned SplitBar.
9031  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
9032  * document.body, make sure you assign an id to the body element.
9033  */
9034 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
9035     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
9036     this.container = Roo.get(container);
9037 };
9038
9039 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
9040     init : function(s){
9041         this.basic.init(s);
9042     },
9043     
9044     getElementSize : function(s){
9045         return this.basic.getElementSize(s);
9046     },
9047     
9048     setElementSize : function(s, newSize, onComplete){
9049         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
9050     },
9051     
9052     moveSplitter : function(s){
9053         var yes = Roo.SplitBar;
9054         switch(s.placement){
9055             case yes.LEFT:
9056                 s.el.setX(s.resizingEl.getRight());
9057                 break;
9058             case yes.RIGHT:
9059                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
9060                 break;
9061             case yes.TOP:
9062                 s.el.setY(s.resizingEl.getBottom());
9063                 break;
9064             case yes.BOTTOM:
9065                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
9066                 break;
9067         }
9068     }
9069 };
9070
9071 /**
9072  * Orientation constant - Create a vertical SplitBar
9073  * @static
9074  * @type Number
9075  */
9076 Roo.SplitBar.VERTICAL = 1;
9077
9078 /**
9079  * Orientation constant - Create a horizontal SplitBar
9080  * @static
9081  * @type Number
9082  */
9083 Roo.SplitBar.HORIZONTAL = 2;
9084
9085 /**
9086  * Placement constant - The resizing element is to the left of the splitter element
9087  * @static
9088  * @type Number
9089  */
9090 Roo.SplitBar.LEFT = 1;
9091
9092 /**
9093  * Placement constant - The resizing element is to the right of the splitter element
9094  * @static
9095  * @type Number
9096  */
9097 Roo.SplitBar.RIGHT = 2;
9098
9099 /**
9100  * Placement constant - The resizing element is positioned above the splitter element
9101  * @static
9102  * @type Number
9103  */
9104 Roo.SplitBar.TOP = 3;
9105
9106 /**
9107  * Placement constant - The resizing element is positioned under splitter element
9108  * @static
9109  * @type Number
9110  */
9111 Roo.SplitBar.BOTTOM = 4;
9112 /*
9113  * Based on:
9114  * Ext JS Library 1.1.1
9115  * Copyright(c) 2006-2007, Ext JS, LLC.
9116  *
9117  * Originally Released Under LGPL - original licence link has changed is not relivant.
9118  *
9119  * Fork - LGPL
9120  * <script type="text/javascript">
9121  */
9122
9123 /**
9124  * @class Roo.View
9125  * @extends Roo.util.Observable
9126  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
9127  * This class also supports single and multi selection modes. <br>
9128  * Create a data model bound view:
9129  <pre><code>
9130  var store = new Roo.data.Store(...);
9131
9132  var view = new Roo.View({
9133     el : "my-element",
9134     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
9135  
9136     singleSelect: true,
9137     selectedClass: "ydataview-selected",
9138     store: store
9139  });
9140
9141  // listen for node click?
9142  view.on("click", function(vw, index, node, e){
9143  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9144  });
9145
9146  // load XML data
9147  dataModel.load("foobar.xml");
9148  </code></pre>
9149  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9150  * <br><br>
9151  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9152  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9153  * 
9154  * Note: old style constructor is still suported (container, template, config)
9155  * 
9156  * @constructor
9157  * Create a new View
9158  * @param {Object} config The config object
9159  * 
9160  */
9161 Roo.View = function(config, depreciated_tpl, depreciated_config){
9162     
9163     if (typeof(depreciated_tpl) == 'undefined') {
9164         // new way.. - universal constructor.
9165         Roo.apply(this, config);
9166         this.el  = Roo.get(this.el);
9167     } else {
9168         // old format..
9169         this.el  = Roo.get(config);
9170         this.tpl = depreciated_tpl;
9171         Roo.apply(this, depreciated_config);
9172     }
9173     this.wrapEl  = this.el.wrap().wrap();
9174     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
9175     
9176     
9177     if(typeof(this.tpl) == "string"){
9178         this.tpl = new Roo.Template(this.tpl);
9179     } else {
9180         // support xtype ctors..
9181         this.tpl = new Roo.factory(this.tpl, Roo);
9182     }
9183     
9184     
9185     this.tpl.compile();
9186    
9187   
9188     
9189      
9190     /** @private */
9191     this.addEvents({
9192         /**
9193          * @event beforeclick
9194          * Fires before a click is processed. Returns false to cancel the default action.
9195          * @param {Roo.View} this
9196          * @param {Number} index The index of the target node
9197          * @param {HTMLElement} node The target node
9198          * @param {Roo.EventObject} e The raw event object
9199          */
9200             "beforeclick" : true,
9201         /**
9202          * @event click
9203          * Fires when a template node is clicked.
9204          * @param {Roo.View} this
9205          * @param {Number} index The index of the target node
9206          * @param {HTMLElement} node The target node
9207          * @param {Roo.EventObject} e The raw event object
9208          */
9209             "click" : true,
9210         /**
9211          * @event dblclick
9212          * Fires when a template node is double clicked.
9213          * @param {Roo.View} this
9214          * @param {Number} index The index of the target node
9215          * @param {HTMLElement} node The target node
9216          * @param {Roo.EventObject} e The raw event object
9217          */
9218             "dblclick" : true,
9219         /**
9220          * @event contextmenu
9221          * Fires when a template node is right clicked.
9222          * @param {Roo.View} this
9223          * @param {Number} index The index of the target node
9224          * @param {HTMLElement} node The target node
9225          * @param {Roo.EventObject} e The raw event object
9226          */
9227             "contextmenu" : true,
9228         /**
9229          * @event selectionchange
9230          * Fires when the selected nodes change.
9231          * @param {Roo.View} this
9232          * @param {Array} selections Array of the selected nodes
9233          */
9234             "selectionchange" : true,
9235     
9236         /**
9237          * @event beforeselect
9238          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9239          * @param {Roo.View} this
9240          * @param {HTMLElement} node The node to be selected
9241          * @param {Array} selections Array of currently selected nodes
9242          */
9243             "beforeselect" : true,
9244         /**
9245          * @event preparedata
9246          * Fires on every row to render, to allow you to change the data.
9247          * @param {Roo.View} this
9248          * @param {Object} data to be rendered (change this)
9249          */
9250           "preparedata" : true
9251           
9252           
9253         });
9254
9255
9256
9257     this.el.on({
9258         "click": this.onClick,
9259         "dblclick": this.onDblClick,
9260         "contextmenu": this.onContextMenu,
9261         scope:this
9262     });
9263
9264     this.selections = [];
9265     this.nodes = [];
9266     this.cmp = new Roo.CompositeElementLite([]);
9267     if(this.store){
9268         this.store = Roo.factory(this.store, Roo.data);
9269         this.setStore(this.store, true);
9270     }
9271     
9272     if ( this.footer && this.footer.xtype) {
9273            
9274          var fctr = this.wrapEl.appendChild(document.createElement("div"));
9275         
9276         this.footer.dataSource = this.store
9277         this.footer.container = fctr;
9278         this.footer = Roo.factory(this.footer, Roo);
9279         fctr.insertFirst(this.el);
9280         
9281         // this is a bit insane - as the paging toolbar seems to detach the el..
9282 //        dom.parentNode.parentNode.parentNode
9283          // they get detached?
9284     }
9285     
9286     
9287     Roo.View.superclass.constructor.call(this);
9288     
9289     
9290 };
9291
9292 Roo.extend(Roo.View, Roo.util.Observable, {
9293     
9294      /**
9295      * @cfg {Roo.data.Store} store Data store to load data from.
9296      */
9297     store : false,
9298     
9299     /**
9300      * @cfg {String|Roo.Element} el The container element.
9301      */
9302     el : '',
9303     
9304     /**
9305      * @cfg {String|Roo.Template} tpl The template used by this View 
9306      */
9307     tpl : false,
9308     /**
9309      * @cfg {String} dataName the named area of the template to use as the data area
9310      *                          Works with domtemplates roo-name="name"
9311      */
9312     dataName: false,
9313     /**
9314      * @cfg {String} selectedClass The css class to add to selected nodes
9315      */
9316     selectedClass : "x-view-selected",
9317      /**
9318      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9319      */
9320     emptyText : "",
9321     
9322     /**
9323      * @cfg {String} text to display on mask (default Loading)
9324      */
9325     mask : false,
9326     /**
9327      * @cfg {Boolean} multiSelect Allow multiple selection
9328      */
9329     multiSelect : false,
9330     /**
9331      * @cfg {Boolean} singleSelect Allow single selection
9332      */
9333     singleSelect:  false,
9334     
9335     /**
9336      * @cfg {Boolean} toggleSelect - selecting 
9337      */
9338     toggleSelect : false,
9339     
9340     /**
9341      * Returns the element this view is bound to.
9342      * @return {Roo.Element}
9343      */
9344     getEl : function(){
9345         return this.wrapEl;
9346     },
9347     
9348     
9349
9350     /**
9351      * Refreshes the view. - called by datachanged on the store. - do not call directly.
9352      */
9353     refresh : function(){
9354         var t = this.tpl;
9355         
9356         // if we are using something like 'domtemplate', then
9357         // the what gets used is:
9358         // t.applySubtemplate(NAME, data, wrapping data..)
9359         // the outer template then get' applied with
9360         //     the store 'extra data'
9361         // and the body get's added to the
9362         //      roo-name="data" node?
9363         //      <span class='roo-tpl-{name}'></span> ?????
9364         
9365         
9366         
9367         this.clearSelections();
9368         this.el.update("");
9369         var html = [];
9370         var records = this.store.getRange();
9371         if(records.length < 1) {
9372             
9373             // is this valid??  = should it render a template??
9374             
9375             this.el.update(this.emptyText);
9376             return;
9377         }
9378         var el = this.el;
9379         if (this.dataName) {
9380             this.el.update(t.apply(this.store.meta)); //????
9381             el = this.el.child('.roo-tpl-' + this.dataName);
9382         }
9383         
9384         for(var i = 0, len = records.length; i < len; i++){
9385             var data = this.prepareData(records[i].data, i, records[i]);
9386             this.fireEvent("preparedata", this, data, i, records[i]);
9387             html[html.length] = Roo.util.Format.trim(
9388                 this.dataName ?
9389                     t.applySubtemplate(this.dataName, data, this.store.meta) :
9390                     t.apply(data)
9391             );
9392         }
9393         
9394         
9395         
9396         el.update(html.join(""));
9397         this.nodes = el.dom.childNodes;
9398         this.updateIndexes(0);
9399     },
9400
9401     /**
9402      * Function to override to reformat the data that is sent to
9403      * the template for each node.
9404      * DEPRICATED - use the preparedata event handler.
9405      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9406      * a JSON object for an UpdateManager bound view).
9407      */
9408     prepareData : function(data, index, record)
9409     {
9410         this.fireEvent("preparedata", this, data, index, record);
9411         return data;
9412     },
9413
9414     onUpdate : function(ds, record){
9415         this.clearSelections();
9416         var index = this.store.indexOf(record);
9417         var n = this.nodes[index];
9418         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
9419         n.parentNode.removeChild(n);
9420         this.updateIndexes(index, index);
9421     },
9422
9423     
9424     
9425 // --------- FIXME     
9426     onAdd : function(ds, records, index)
9427     {
9428         this.clearSelections();
9429         if(this.nodes.length == 0){
9430             this.refresh();
9431             return;
9432         }
9433         var n = this.nodes[index];
9434         for(var i = 0, len = records.length; i < len; i++){
9435             var d = this.prepareData(records[i].data, i, records[i]);
9436             if(n){
9437                 this.tpl.insertBefore(n, d);
9438             }else{
9439                 
9440                 this.tpl.append(this.el, d);
9441             }
9442         }
9443         this.updateIndexes(index);
9444     },
9445
9446     onRemove : function(ds, record, index){
9447         this.clearSelections();
9448         var el = this.dataName  ?
9449             this.el.child('.roo-tpl-' + this.dataName) :
9450             this.el; 
9451         el.dom.removeChild(this.nodes[index]);
9452         this.updateIndexes(index);
9453     },
9454
9455     /**
9456      * Refresh an individual node.
9457      * @param {Number} index
9458      */
9459     refreshNode : function(index){
9460         this.onUpdate(this.store, this.store.getAt(index));
9461     },
9462
9463     updateIndexes : function(startIndex, endIndex){
9464         var ns = this.nodes;
9465         startIndex = startIndex || 0;
9466         endIndex = endIndex || ns.length - 1;
9467         for(var i = startIndex; i <= endIndex; i++){
9468             ns[i].nodeIndex = i;
9469         }
9470     },
9471
9472     /**
9473      * Changes the data store this view uses and refresh the view.
9474      * @param {Store} store
9475      */
9476     setStore : function(store, initial){
9477         if(!initial && this.store){
9478             this.store.un("datachanged", this.refresh);
9479             this.store.un("add", this.onAdd);
9480             this.store.un("remove", this.onRemove);
9481             this.store.un("update", this.onUpdate);
9482             this.store.un("clear", this.refresh);
9483             this.store.un("beforeload", this.onBeforeLoad);
9484             this.store.un("load", this.onLoad);
9485             this.store.un("loadexception", this.onLoad);
9486         }
9487         if(store){
9488           
9489             store.on("datachanged", this.refresh, this);
9490             store.on("add", this.onAdd, this);
9491             store.on("remove", this.onRemove, this);
9492             store.on("update", this.onUpdate, this);
9493             store.on("clear", this.refresh, this);
9494             store.on("beforeload", this.onBeforeLoad, this);
9495             store.on("load", this.onLoad, this);
9496             store.on("loadexception", this.onLoad, this);
9497         }
9498         
9499         if(store){
9500             this.refresh();
9501         }
9502     },
9503     /**
9504      * onbeforeLoad - masks the loading area.
9505      *
9506      */
9507     onBeforeLoad : function()
9508     {
9509         this.el.update("");
9510         this.el.mask(this.mask ? this.mask : "Loading" ); 
9511     },
9512     onLoad : function ()
9513     {
9514         this.el.unmask();
9515     },
9516     
9517
9518     /**
9519      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9520      * @param {HTMLElement} node
9521      * @return {HTMLElement} The template node
9522      */
9523     findItemFromChild : function(node){
9524         var el = this.dataName  ?
9525             this.el.child('.roo-tpl-' + this.dataName,true) :
9526             this.el.dom; 
9527         
9528         if(!node || node.parentNode == el){
9529                     return node;
9530             }
9531             var p = node.parentNode;
9532             while(p && p != el){
9533             if(p.parentNode == el){
9534                 return p;
9535             }
9536             p = p.parentNode;
9537         }
9538             return null;
9539     },
9540
9541     /** @ignore */
9542     onClick : function(e){
9543         var item = this.findItemFromChild(e.getTarget());
9544         if(item){
9545             var index = this.indexOf(item);
9546             if(this.onItemClick(item, index, e) !== false){
9547                 this.fireEvent("click", this, index, item, e);
9548             }
9549         }else{
9550             this.clearSelections();
9551         }
9552     },
9553
9554     /** @ignore */
9555     onContextMenu : function(e){
9556         var item = this.findItemFromChild(e.getTarget());
9557         if(item){
9558             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9559         }
9560     },
9561
9562     /** @ignore */
9563     onDblClick : function(e){
9564         var item = this.findItemFromChild(e.getTarget());
9565         if(item){
9566             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9567         }
9568     },
9569
9570     onItemClick : function(item, index, e)
9571     {
9572         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9573             return false;
9574         }
9575         if (this.toggleSelect) {
9576             var m = this.isSelected(item) ? 'unselect' : 'select';
9577             Roo.log(m);
9578             var _t = this;
9579             _t[m](item, true, false);
9580             return true;
9581         }
9582         if(this.multiSelect || this.singleSelect){
9583             if(this.multiSelect && e.shiftKey && this.lastSelection){
9584                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9585             }else{
9586                 this.select(item, this.multiSelect && e.ctrlKey);
9587                 this.lastSelection = item;
9588             }
9589             e.preventDefault();
9590         }
9591         return true;
9592     },
9593
9594     /**
9595      * Get the number of selected nodes.
9596      * @return {Number}
9597      */
9598     getSelectionCount : function(){
9599         return this.selections.length;
9600     },
9601
9602     /**
9603      * Get the currently selected nodes.
9604      * @return {Array} An array of HTMLElements
9605      */
9606     getSelectedNodes : function(){
9607         return this.selections;
9608     },
9609
9610     /**
9611      * Get the indexes of the selected nodes.
9612      * @return {Array}
9613      */
9614     getSelectedIndexes : function(){
9615         var indexes = [], s = this.selections;
9616         for(var i = 0, len = s.length; i < len; i++){
9617             indexes.push(s[i].nodeIndex);
9618         }
9619         return indexes;
9620     },
9621
9622     /**
9623      * Clear all selections
9624      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9625      */
9626     clearSelections : function(suppressEvent){
9627         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9628             this.cmp.elements = this.selections;
9629             this.cmp.removeClass(this.selectedClass);
9630             this.selections = [];
9631             if(!suppressEvent){
9632                 this.fireEvent("selectionchange", this, this.selections);
9633             }
9634         }
9635     },
9636
9637     /**
9638      * Returns true if the passed node is selected
9639      * @param {HTMLElement/Number} node The node or node index
9640      * @return {Boolean}
9641      */
9642     isSelected : function(node){
9643         var s = this.selections;
9644         if(s.length < 1){
9645             return false;
9646         }
9647         node = this.getNode(node);
9648         return s.indexOf(node) !== -1;
9649     },
9650
9651     /**
9652      * Selects nodes.
9653      * @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
9654      * @param {Boolean} keepExisting (optional) true to keep existing selections
9655      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9656      */
9657     select : function(nodeInfo, keepExisting, suppressEvent){
9658         if(nodeInfo instanceof Array){
9659             if(!keepExisting){
9660                 this.clearSelections(true);
9661             }
9662             for(var i = 0, len = nodeInfo.length; i < len; i++){
9663                 this.select(nodeInfo[i], true, true);
9664             }
9665             return;
9666         } 
9667         var node = this.getNode(nodeInfo);
9668         if(!node || this.isSelected(node)){
9669             return; // already selected.
9670         }
9671         if(!keepExisting){
9672             this.clearSelections(true);
9673         }
9674         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9675             Roo.fly(node).addClass(this.selectedClass);
9676             this.selections.push(node);
9677             if(!suppressEvent){
9678                 this.fireEvent("selectionchange", this, this.selections);
9679             }
9680         }
9681         
9682         
9683     },
9684       /**
9685      * Unselects nodes.
9686      * @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
9687      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
9688      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9689      */
9690     unselect : function(nodeInfo, keepExisting, suppressEvent)
9691     {
9692         if(nodeInfo instanceof Array){
9693             Roo.each(this.selections, function(s) {
9694                 this.unselect(s, nodeInfo);
9695             }, this);
9696             return;
9697         }
9698         var node = this.getNode(nodeInfo);
9699         if(!node || !this.isSelected(node)){
9700             Roo.log("not selected");
9701             return; // not selected.
9702         }
9703         // fireevent???
9704         var ns = [];
9705         Roo.each(this.selections, function(s) {
9706             if (s == node ) {
9707                 Roo.fly(node).removeClass(this.selectedClass);
9708
9709                 return;
9710             }
9711             ns.push(s);
9712         },this);
9713         
9714         this.selections= ns;
9715         this.fireEvent("selectionchange", this, this.selections);
9716     },
9717
9718     /**
9719      * Gets a template node.
9720      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9721      * @return {HTMLElement} The node or null if it wasn't found
9722      */
9723     getNode : function(nodeInfo){
9724         if(typeof nodeInfo == "string"){
9725             return document.getElementById(nodeInfo);
9726         }else if(typeof nodeInfo == "number"){
9727             return this.nodes[nodeInfo];
9728         }
9729         return nodeInfo;
9730     },
9731
9732     /**
9733      * Gets a range template nodes.
9734      * @param {Number} startIndex
9735      * @param {Number} endIndex
9736      * @return {Array} An array of nodes
9737      */
9738     getNodes : function(start, end){
9739         var ns = this.nodes;
9740         start = start || 0;
9741         end = typeof end == "undefined" ? ns.length - 1 : end;
9742         var nodes = [];
9743         if(start <= end){
9744             for(var i = start; i <= end; i++){
9745                 nodes.push(ns[i]);
9746             }
9747         } else{
9748             for(var i = start; i >= end; i--){
9749                 nodes.push(ns[i]);
9750             }
9751         }
9752         return nodes;
9753     },
9754
9755     /**
9756      * Finds the index of the passed node
9757      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9758      * @return {Number} The index of the node or -1
9759      */
9760     indexOf : function(node){
9761         node = this.getNode(node);
9762         if(typeof node.nodeIndex == "number"){
9763             return node.nodeIndex;
9764         }
9765         var ns = this.nodes;
9766         for(var i = 0, len = ns.length; i < len; i++){
9767             if(ns[i] == node){
9768                 return i;
9769             }
9770         }
9771         return -1;
9772     }
9773 });
9774 /*
9775  * Based on:
9776  * Ext JS Library 1.1.1
9777  * Copyright(c) 2006-2007, Ext JS, LLC.
9778  *
9779  * Originally Released Under LGPL - original licence link has changed is not relivant.
9780  *
9781  * Fork - LGPL
9782  * <script type="text/javascript">
9783  */
9784
9785 /**
9786  * @class Roo.JsonView
9787  * @extends Roo.View
9788  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9789 <pre><code>
9790 var view = new Roo.JsonView({
9791     container: "my-element",
9792     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
9793     multiSelect: true, 
9794     jsonRoot: "data" 
9795 });
9796
9797 // listen for node click?
9798 view.on("click", function(vw, index, node, e){
9799     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9800 });
9801
9802 // direct load of JSON data
9803 view.load("foobar.php");
9804
9805 // Example from my blog list
9806 var tpl = new Roo.Template(
9807     '&lt;div class="entry"&gt;' +
9808     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
9809     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
9810     "&lt;/div&gt;&lt;hr /&gt;"
9811 );
9812
9813 var moreView = new Roo.JsonView({
9814     container :  "entry-list", 
9815     template : tpl,
9816     jsonRoot: "posts"
9817 });
9818 moreView.on("beforerender", this.sortEntries, this);
9819 moreView.load({
9820     url: "/blog/get-posts.php",
9821     params: "allposts=true",
9822     text: "Loading Blog Entries..."
9823 });
9824 </code></pre>
9825
9826 * Note: old code is supported with arguments : (container, template, config)
9827
9828
9829  * @constructor
9830  * Create a new JsonView
9831  * 
9832  * @param {Object} config The config object
9833  * 
9834  */
9835 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9836     
9837     
9838     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9839
9840     var um = this.el.getUpdateManager();
9841     um.setRenderer(this);
9842     um.on("update", this.onLoad, this);
9843     um.on("failure", this.onLoadException, this);
9844
9845     /**
9846      * @event beforerender
9847      * Fires before rendering of the downloaded JSON data.
9848      * @param {Roo.JsonView} this
9849      * @param {Object} data The JSON data loaded
9850      */
9851     /**
9852      * @event load
9853      * Fires when data is loaded.
9854      * @param {Roo.JsonView} this
9855      * @param {Object} data The JSON data loaded
9856      * @param {Object} response The raw Connect response object
9857      */
9858     /**
9859      * @event loadexception
9860      * Fires when loading fails.
9861      * @param {Roo.JsonView} this
9862      * @param {Object} response The raw Connect response object
9863      */
9864     this.addEvents({
9865         'beforerender' : true,
9866         'load' : true,
9867         'loadexception' : true
9868     });
9869 };
9870 Roo.extend(Roo.JsonView, Roo.View, {
9871     /**
9872      * @type {String} The root property in the loaded JSON object that contains the data
9873      */
9874     jsonRoot : "",
9875
9876     /**
9877      * Refreshes the view.
9878      */
9879     refresh : function(){
9880         this.clearSelections();
9881         this.el.update("");
9882         var html = [];
9883         var o = this.jsonData;
9884         if(o && o.length > 0){
9885             for(var i = 0, len = o.length; i < len; i++){
9886                 var data = this.prepareData(o[i], i, o);
9887                 html[html.length] = this.tpl.apply(data);
9888             }
9889         }else{
9890             html.push(this.emptyText);
9891         }
9892         this.el.update(html.join(""));
9893         this.nodes = this.el.dom.childNodes;
9894         this.updateIndexes(0);
9895     },
9896
9897     /**
9898      * 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.
9899      * @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:
9900      <pre><code>
9901      view.load({
9902          url: "your-url.php",
9903          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9904          callback: yourFunction,
9905          scope: yourObject, //(optional scope)
9906          discardUrl: false,
9907          nocache: false,
9908          text: "Loading...",
9909          timeout: 30,
9910          scripts: false
9911      });
9912      </code></pre>
9913      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9914      * 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.
9915      * @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}
9916      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9917      * @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.
9918      */
9919     load : function(){
9920         var um = this.el.getUpdateManager();
9921         um.update.apply(um, arguments);
9922     },
9923
9924     render : function(el, response){
9925         this.clearSelections();
9926         this.el.update("");
9927         var o;
9928         try{
9929             o = Roo.util.JSON.decode(response.responseText);
9930             if(this.jsonRoot){
9931                 
9932                 o = o[this.jsonRoot];
9933             }
9934         } catch(e){
9935         }
9936         /**
9937          * The current JSON data or null
9938          */
9939         this.jsonData = o;
9940         this.beforeRender();
9941         this.refresh();
9942     },
9943
9944 /**
9945  * Get the number of records in the current JSON dataset
9946  * @return {Number}
9947  */
9948     getCount : function(){
9949         return this.jsonData ? this.jsonData.length : 0;
9950     },
9951
9952 /**
9953  * Returns the JSON object for the specified node(s)
9954  * @param {HTMLElement/Array} node The node or an array of nodes
9955  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9956  * you get the JSON object for the node
9957  */
9958     getNodeData : function(node){
9959         if(node instanceof Array){
9960             var data = [];
9961             for(var i = 0, len = node.length; i < len; i++){
9962                 data.push(this.getNodeData(node[i]));
9963             }
9964             return data;
9965         }
9966         return this.jsonData[this.indexOf(node)] || null;
9967     },
9968
9969     beforeRender : function(){
9970         this.snapshot = this.jsonData;
9971         if(this.sortInfo){
9972             this.sort.apply(this, this.sortInfo);
9973         }
9974         this.fireEvent("beforerender", this, this.jsonData);
9975     },
9976
9977     onLoad : function(el, o){
9978         this.fireEvent("load", this, this.jsonData, o);
9979     },
9980
9981     onLoadException : function(el, o){
9982         this.fireEvent("loadexception", this, o);
9983     },
9984
9985 /**
9986  * Filter the data by a specific property.
9987  * @param {String} property A property on your JSON objects
9988  * @param {String/RegExp} value Either string that the property values
9989  * should start with, or a RegExp to test against the property
9990  */
9991     filter : function(property, value){
9992         if(this.jsonData){
9993             var data = [];
9994             var ss = this.snapshot;
9995             if(typeof value == "string"){
9996                 var vlen = value.length;
9997                 if(vlen == 0){
9998                     this.clearFilter();
9999                     return;
10000                 }
10001                 value = value.toLowerCase();
10002                 for(var i = 0, len = ss.length; i < len; i++){
10003                     var o = ss[i];
10004                     if(o[property].substr(0, vlen).toLowerCase() == value){
10005                         data.push(o);
10006                     }
10007                 }
10008             } else if(value.exec){ // regex?
10009                 for(var i = 0, len = ss.length; i < len; i++){
10010                     var o = ss[i];
10011                     if(value.test(o[property])){
10012                         data.push(o);
10013                     }
10014                 }
10015             } else{
10016                 return;
10017             }
10018             this.jsonData = data;
10019             this.refresh();
10020         }
10021     },
10022
10023 /**
10024  * Filter by a function. The passed function will be called with each
10025  * object in the current dataset. If the function returns true the value is kept,
10026  * otherwise it is filtered.
10027  * @param {Function} fn
10028  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
10029  */
10030     filterBy : function(fn, scope){
10031         if(this.jsonData){
10032             var data = [];
10033             var ss = this.snapshot;
10034             for(var i = 0, len = ss.length; i < len; i++){
10035                 var o = ss[i];
10036                 if(fn.call(scope || this, o)){
10037                     data.push(o);
10038                 }
10039             }
10040             this.jsonData = data;
10041             this.refresh();
10042         }
10043     },
10044
10045 /**
10046  * Clears the current filter.
10047  */
10048     clearFilter : function(){
10049         if(this.snapshot && this.jsonData != this.snapshot){
10050             this.jsonData = this.snapshot;
10051             this.refresh();
10052         }
10053     },
10054
10055
10056 /**
10057  * Sorts the data for this view and refreshes it.
10058  * @param {String} property A property on your JSON objects to sort on
10059  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
10060  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
10061  */
10062     sort : function(property, dir, sortType){
10063         this.sortInfo = Array.prototype.slice.call(arguments, 0);
10064         if(this.jsonData){
10065             var p = property;
10066             var dsc = dir && dir.toLowerCase() == "desc";
10067             var f = function(o1, o2){
10068                 var v1 = sortType ? sortType(o1[p]) : o1[p];
10069                 var v2 = sortType ? sortType(o2[p]) : o2[p];
10070                 ;
10071                 if(v1 < v2){
10072                     return dsc ? +1 : -1;
10073                 } else if(v1 > v2){
10074                     return dsc ? -1 : +1;
10075                 } else{
10076                     return 0;
10077                 }
10078             };
10079             this.jsonData.sort(f);
10080             this.refresh();
10081             if(this.jsonData != this.snapshot){
10082                 this.snapshot.sort(f);
10083             }
10084         }
10085     }
10086 });/*
10087  * Based on:
10088  * Ext JS Library 1.1.1
10089  * Copyright(c) 2006-2007, Ext JS, LLC.
10090  *
10091  * Originally Released Under LGPL - original licence link has changed is not relivant.
10092  *
10093  * Fork - LGPL
10094  * <script type="text/javascript">
10095  */
10096  
10097
10098 /**
10099  * @class Roo.ColorPalette
10100  * @extends Roo.Component
10101  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
10102  * Here's an example of typical usage:
10103  * <pre><code>
10104 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
10105 cp.render('my-div');
10106
10107 cp.on('select', function(palette, selColor){
10108     // do something with selColor
10109 });
10110 </code></pre>
10111  * @constructor
10112  * Create a new ColorPalette
10113  * @param {Object} config The config object
10114  */
10115 Roo.ColorPalette = function(config){
10116     Roo.ColorPalette.superclass.constructor.call(this, config);
10117     this.addEvents({
10118         /**
10119              * @event select
10120              * Fires when a color is selected
10121              * @param {ColorPalette} this
10122              * @param {String} color The 6-digit color hex code (without the # symbol)
10123              */
10124         select: true
10125     });
10126
10127     if(this.handler){
10128         this.on("select", this.handler, this.scope, true);
10129     }
10130 };
10131 Roo.extend(Roo.ColorPalette, Roo.Component, {
10132     /**
10133      * @cfg {String} itemCls
10134      * The CSS class to apply to the containing element (defaults to "x-color-palette")
10135      */
10136     itemCls : "x-color-palette",
10137     /**
10138      * @cfg {String} value
10139      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
10140      * the hex codes are case-sensitive.
10141      */
10142     value : null,
10143     clickEvent:'click',
10144     // private
10145     ctype: "Roo.ColorPalette",
10146
10147     /**
10148      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
10149      */
10150     allowReselect : false,
10151
10152     /**
10153      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
10154      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
10155      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
10156      * of colors with the width setting until the box is symmetrical.</p>
10157      * <p>You can override individual colors if needed:</p>
10158      * <pre><code>
10159 var cp = new Roo.ColorPalette();
10160 cp.colors[0] = "FF0000";  // change the first box to red
10161 </code></pre>
10162
10163 Or you can provide a custom array of your own for complete control:
10164 <pre><code>
10165 var cp = new Roo.ColorPalette();
10166 cp.colors = ["000000", "993300", "333300"];
10167 </code></pre>
10168      * @type Array
10169      */
10170     colors : [
10171         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
10172         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
10173         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
10174         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
10175         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
10176     ],
10177
10178     // private
10179     onRender : function(container, position){
10180         var t = new Roo.MasterTemplate(
10181             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
10182         );
10183         var c = this.colors;
10184         for(var i = 0, len = c.length; i < len; i++){
10185             t.add([c[i]]);
10186         }
10187         var el = document.createElement("div");
10188         el.className = this.itemCls;
10189         t.overwrite(el);
10190         container.dom.insertBefore(el, position);
10191         this.el = Roo.get(el);
10192         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
10193         if(this.clickEvent != 'click'){
10194             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
10195         }
10196     },
10197
10198     // private
10199     afterRender : function(){
10200         Roo.ColorPalette.superclass.afterRender.call(this);
10201         if(this.value){
10202             var s = this.value;
10203             this.value = null;
10204             this.select(s);
10205         }
10206     },
10207
10208     // private
10209     handleClick : function(e, t){
10210         e.preventDefault();
10211         if(!this.disabled){
10212             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
10213             this.select(c.toUpperCase());
10214         }
10215     },
10216
10217     /**
10218      * Selects the specified color in the palette (fires the select event)
10219      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
10220      */
10221     select : function(color){
10222         color = color.replace("#", "");
10223         if(color != this.value || this.allowReselect){
10224             var el = this.el;
10225             if(this.value){
10226                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
10227             }
10228             el.child("a.color-"+color).addClass("x-color-palette-sel");
10229             this.value = color;
10230             this.fireEvent("select", this, color);
10231         }
10232     }
10233 });/*
10234  * Based on:
10235  * Ext JS Library 1.1.1
10236  * Copyright(c) 2006-2007, Ext JS, LLC.
10237  *
10238  * Originally Released Under LGPL - original licence link has changed is not relivant.
10239  *
10240  * Fork - LGPL
10241  * <script type="text/javascript">
10242  */
10243  
10244 /**
10245  * @class Roo.DatePicker
10246  * @extends Roo.Component
10247  * Simple date picker class.
10248  * @constructor
10249  * Create a new DatePicker
10250  * @param {Object} config The config object
10251  */
10252 Roo.DatePicker = function(config){
10253     Roo.DatePicker.superclass.constructor.call(this, config);
10254
10255     this.value = config && config.value ?
10256                  config.value.clearTime() : new Date().clearTime();
10257
10258     this.addEvents({
10259         /**
10260              * @event select
10261              * Fires when a date is selected
10262              * @param {DatePicker} this
10263              * @param {Date} date The selected date
10264              */
10265         'select': true,
10266         /**
10267              * @event monthchange
10268              * Fires when the displayed month changes 
10269              * @param {DatePicker} this
10270              * @param {Date} date The selected month
10271              */
10272         'monthchange': true
10273     });
10274
10275     if(this.handler){
10276         this.on("select", this.handler,  this.scope || this);
10277     }
10278     // build the disabledDatesRE
10279     if(!this.disabledDatesRE && this.disabledDates){
10280         var dd = this.disabledDates;
10281         var re = "(?:";
10282         for(var i = 0; i < dd.length; i++){
10283             re += dd[i];
10284             if(i != dd.length-1) re += "|";
10285         }
10286         this.disabledDatesRE = new RegExp(re + ")");
10287     }
10288 };
10289
10290 Roo.extend(Roo.DatePicker, Roo.Component, {
10291     /**
10292      * @cfg {String} todayText
10293      * The text to display on the button that selects the current date (defaults to "Today")
10294      */
10295     todayText : "Today",
10296     /**
10297      * @cfg {String} okText
10298      * The text to display on the ok button
10299      */
10300     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
10301     /**
10302      * @cfg {String} cancelText
10303      * The text to display on the cancel button
10304      */
10305     cancelText : "Cancel",
10306     /**
10307      * @cfg {String} todayTip
10308      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
10309      */
10310     todayTip : "{0} (Spacebar)",
10311     /**
10312      * @cfg {Date} minDate
10313      * Minimum allowable date (JavaScript date object, defaults to null)
10314      */
10315     minDate : null,
10316     /**
10317      * @cfg {Date} maxDate
10318      * Maximum allowable date (JavaScript date object, defaults to null)
10319      */
10320     maxDate : null,
10321     /**
10322      * @cfg {String} minText
10323      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
10324      */
10325     minText : "This date is before the minimum date",
10326     /**
10327      * @cfg {String} maxText
10328      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
10329      */
10330     maxText : "This date is after the maximum date",
10331     /**
10332      * @cfg {String} format
10333      * The default date format string which can be overriden for localization support.  The format must be
10334      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
10335      */
10336     format : "m/d/y",
10337     /**
10338      * @cfg {Array} disabledDays
10339      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
10340      */
10341     disabledDays : null,
10342     /**
10343      * @cfg {String} disabledDaysText
10344      * The tooltip to display when the date falls on a disabled day (defaults to "")
10345      */
10346     disabledDaysText : "",
10347     /**
10348      * @cfg {RegExp} disabledDatesRE
10349      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
10350      */
10351     disabledDatesRE : null,
10352     /**
10353      * @cfg {String} disabledDatesText
10354      * The tooltip text to display when the date falls on a disabled date (defaults to "")
10355      */
10356     disabledDatesText : "",
10357     /**
10358      * @cfg {Boolean} constrainToViewport
10359      * True to constrain the date picker to the viewport (defaults to true)
10360      */
10361     constrainToViewport : true,
10362     /**
10363      * @cfg {Array} monthNames
10364      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
10365      */
10366     monthNames : Date.monthNames,
10367     /**
10368      * @cfg {Array} dayNames
10369      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
10370      */
10371     dayNames : Date.dayNames,
10372     /**
10373      * @cfg {String} nextText
10374      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
10375      */
10376     nextText: 'Next Month (Control+Right)',
10377     /**
10378      * @cfg {String} prevText
10379      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
10380      */
10381     prevText: 'Previous Month (Control+Left)',
10382     /**
10383      * @cfg {String} monthYearText
10384      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
10385      */
10386     monthYearText: 'Choose a month (Control+Up/Down to move years)',
10387     /**
10388      * @cfg {Number} startDay
10389      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
10390      */
10391     startDay : 0,
10392     /**
10393      * @cfg {Bool} showClear
10394      * Show a clear button (usefull for date form elements that can be blank.)
10395      */
10396     
10397     showClear: false,
10398     
10399     /**
10400      * Sets the value of the date field
10401      * @param {Date} value The date to set
10402      */
10403     setValue : function(value){
10404         var old = this.value;
10405         
10406         if (typeof(value) == 'string') {
10407          
10408             value = Date.parseDate(value, this.format);
10409         }
10410         if (!value) {
10411             value = new Date();
10412         }
10413         
10414         this.value = value.clearTime(true);
10415         if(this.el){
10416             this.update(this.value);
10417         }
10418     },
10419
10420     /**
10421      * Gets the current selected value of the date field
10422      * @return {Date} The selected date
10423      */
10424     getValue : function(){
10425         return this.value;
10426     },
10427
10428     // private
10429     focus : function(){
10430         if(this.el){
10431             this.update(this.activeDate);
10432         }
10433     },
10434
10435     // privateval
10436     onRender : function(container, position){
10437         
10438         var m = [
10439              '<table cellspacing="0">',
10440                 '<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>',
10441                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
10442         var dn = this.dayNames;
10443         for(var i = 0; i < 7; i++){
10444             var d = this.startDay+i;
10445             if(d > 6){
10446                 d = d-7;
10447             }
10448             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
10449         }
10450         m[m.length] = "</tr></thead><tbody><tr>";
10451         for(var i = 0; i < 42; i++) {
10452             if(i % 7 == 0 && i != 0){
10453                 m[m.length] = "</tr><tr>";
10454             }
10455             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
10456         }
10457         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
10458             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
10459
10460         var el = document.createElement("div");
10461         el.className = "x-date-picker";
10462         el.innerHTML = m.join("");
10463
10464         container.dom.insertBefore(el, position);
10465
10466         this.el = Roo.get(el);
10467         this.eventEl = Roo.get(el.firstChild);
10468
10469         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
10470             handler: this.showPrevMonth,
10471             scope: this,
10472             preventDefault:true,
10473             stopDefault:true
10474         });
10475
10476         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
10477             handler: this.showNextMonth,
10478             scope: this,
10479             preventDefault:true,
10480             stopDefault:true
10481         });
10482
10483         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
10484
10485         this.monthPicker = this.el.down('div.x-date-mp');
10486         this.monthPicker.enableDisplayMode('block');
10487         
10488         var kn = new Roo.KeyNav(this.eventEl, {
10489             "left" : function(e){
10490                 e.ctrlKey ?
10491                     this.showPrevMonth() :
10492                     this.update(this.activeDate.add("d", -1));
10493             },
10494
10495             "right" : function(e){
10496                 e.ctrlKey ?
10497                     this.showNextMonth() :
10498                     this.update(this.activeDate.add("d", 1));
10499             },
10500
10501             "up" : function(e){
10502                 e.ctrlKey ?
10503                     this.showNextYear() :
10504                     this.update(this.activeDate.add("d", -7));
10505             },
10506
10507             "down" : function(e){
10508                 e.ctrlKey ?
10509                     this.showPrevYear() :
10510                     this.update(this.activeDate.add("d", 7));
10511             },
10512
10513             "pageUp" : function(e){
10514                 this.showNextMonth();
10515             },
10516
10517             "pageDown" : function(e){
10518                 this.showPrevMonth();
10519             },
10520
10521             "enter" : function(e){
10522                 e.stopPropagation();
10523                 return true;
10524             },
10525
10526             scope : this
10527         });
10528
10529         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
10530
10531         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
10532
10533         this.el.unselectable();
10534         
10535         this.cells = this.el.select("table.x-date-inner tbody td");
10536         this.textNodes = this.el.query("table.x-date-inner tbody span");
10537
10538         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
10539             text: "&#160;",
10540             tooltip: this.monthYearText
10541         });
10542
10543         this.mbtn.on('click', this.showMonthPicker, this);
10544         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
10545
10546
10547         var today = (new Date()).dateFormat(this.format);
10548         
10549         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
10550         if (this.showClear) {
10551             baseTb.add( new Roo.Toolbar.Fill());
10552         }
10553         baseTb.add({
10554             text: String.format(this.todayText, today),
10555             tooltip: String.format(this.todayTip, today),
10556             handler: this.selectToday,
10557             scope: this
10558         });
10559         
10560         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
10561             
10562         //});
10563         if (this.showClear) {
10564             
10565             baseTb.add( new Roo.Toolbar.Fill());
10566             baseTb.add({
10567                 text: '&#160;',
10568                 cls: 'x-btn-icon x-btn-clear',
10569                 handler: function() {
10570                     //this.value = '';
10571                     this.fireEvent("select", this, '');
10572                 },
10573                 scope: this
10574             });
10575         }
10576         
10577         
10578         if(Roo.isIE){
10579             this.el.repaint();
10580         }
10581         this.update(this.value);
10582     },
10583
10584     createMonthPicker : function(){
10585         if(!this.monthPicker.dom.firstChild){
10586             var buf = ['<table border="0" cellspacing="0">'];
10587             for(var i = 0; i < 6; i++){
10588                 buf.push(
10589                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
10590                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
10591                     i == 0 ?
10592                     '<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>' :
10593                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
10594                 );
10595             }
10596             buf.push(
10597                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
10598                     this.okText,
10599                     '</button><button type="button" class="x-date-mp-cancel">',
10600                     this.cancelText,
10601                     '</button></td></tr>',
10602                 '</table>'
10603             );
10604             this.monthPicker.update(buf.join(''));
10605             this.monthPicker.on('click', this.onMonthClick, this);
10606             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
10607
10608             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
10609             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
10610
10611             this.mpMonths.each(function(m, a, i){
10612                 i += 1;
10613                 if((i%2) == 0){
10614                     m.dom.xmonth = 5 + Math.round(i * .5);
10615                 }else{
10616                     m.dom.xmonth = Math.round((i-1) * .5);
10617                 }
10618             });
10619         }
10620     },
10621
10622     showMonthPicker : function(){
10623         this.createMonthPicker();
10624         var size = this.el.getSize();
10625         this.monthPicker.setSize(size);
10626         this.monthPicker.child('table').setSize(size);
10627
10628         this.mpSelMonth = (this.activeDate || this.value).getMonth();
10629         this.updateMPMonth(this.mpSelMonth);
10630         this.mpSelYear = (this.activeDate || this.value).getFullYear();
10631         this.updateMPYear(this.mpSelYear);
10632
10633         this.monthPicker.slideIn('t', {duration:.2});
10634     },
10635
10636     updateMPYear : function(y){
10637         this.mpyear = y;
10638         var ys = this.mpYears.elements;
10639         for(var i = 1; i <= 10; i++){
10640             var td = ys[i-1], y2;
10641             if((i%2) == 0){
10642                 y2 = y + Math.round(i * .5);
10643                 td.firstChild.innerHTML = y2;
10644                 td.xyear = y2;
10645             }else{
10646                 y2 = y - (5-Math.round(i * .5));
10647                 td.firstChild.innerHTML = y2;
10648                 td.xyear = y2;
10649             }
10650             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
10651         }
10652     },
10653
10654     updateMPMonth : function(sm){
10655         this.mpMonths.each(function(m, a, i){
10656             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
10657         });
10658     },
10659
10660     selectMPMonth: function(m){
10661         
10662     },
10663
10664     onMonthClick : function(e, t){
10665         e.stopEvent();
10666         var el = new Roo.Element(t), pn;
10667         if(el.is('button.x-date-mp-cancel')){
10668             this.hideMonthPicker();
10669         }
10670         else if(el.is('button.x-date-mp-ok')){
10671             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10672             this.hideMonthPicker();
10673         }
10674         else if(pn = el.up('td.x-date-mp-month', 2)){
10675             this.mpMonths.removeClass('x-date-mp-sel');
10676             pn.addClass('x-date-mp-sel');
10677             this.mpSelMonth = pn.dom.xmonth;
10678         }
10679         else if(pn = el.up('td.x-date-mp-year', 2)){
10680             this.mpYears.removeClass('x-date-mp-sel');
10681             pn.addClass('x-date-mp-sel');
10682             this.mpSelYear = pn.dom.xyear;
10683         }
10684         else if(el.is('a.x-date-mp-prev')){
10685             this.updateMPYear(this.mpyear-10);
10686         }
10687         else if(el.is('a.x-date-mp-next')){
10688             this.updateMPYear(this.mpyear+10);
10689         }
10690     },
10691
10692     onMonthDblClick : function(e, t){
10693         e.stopEvent();
10694         var el = new Roo.Element(t), pn;
10695         if(pn = el.up('td.x-date-mp-month', 2)){
10696             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
10697             this.hideMonthPicker();
10698         }
10699         else if(pn = el.up('td.x-date-mp-year', 2)){
10700             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10701             this.hideMonthPicker();
10702         }
10703     },
10704
10705     hideMonthPicker : function(disableAnim){
10706         if(this.monthPicker){
10707             if(disableAnim === true){
10708                 this.monthPicker.hide();
10709             }else{
10710                 this.monthPicker.slideOut('t', {duration:.2});
10711             }
10712         }
10713     },
10714
10715     // private
10716     showPrevMonth : function(e){
10717         this.update(this.activeDate.add("mo", -1));
10718     },
10719
10720     // private
10721     showNextMonth : function(e){
10722         this.update(this.activeDate.add("mo", 1));
10723     },
10724
10725     // private
10726     showPrevYear : function(){
10727         this.update(this.activeDate.add("y", -1));
10728     },
10729
10730     // private
10731     showNextYear : function(){
10732         this.update(this.activeDate.add("y", 1));
10733     },
10734
10735     // private
10736     handleMouseWheel : function(e){
10737         var delta = e.getWheelDelta();
10738         if(delta > 0){
10739             this.showPrevMonth();
10740             e.stopEvent();
10741         } else if(delta < 0){
10742             this.showNextMonth();
10743             e.stopEvent();
10744         }
10745     },
10746
10747     // private
10748     handleDateClick : function(e, t){
10749         e.stopEvent();
10750         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10751             this.setValue(new Date(t.dateValue));
10752             this.fireEvent("select", this, this.value);
10753         }
10754     },
10755
10756     // private
10757     selectToday : function(){
10758         this.setValue(new Date().clearTime());
10759         this.fireEvent("select", this, this.value);
10760     },
10761
10762     // private
10763     update : function(date)
10764     {
10765         var vd = this.activeDate;
10766         this.activeDate = date;
10767         if(vd && this.el){
10768             var t = date.getTime();
10769             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10770                 this.cells.removeClass("x-date-selected");
10771                 this.cells.each(function(c){
10772                    if(c.dom.firstChild.dateValue == t){
10773                        c.addClass("x-date-selected");
10774                        setTimeout(function(){
10775                             try{c.dom.firstChild.focus();}catch(e){}
10776                        }, 50);
10777                        return false;
10778                    }
10779                 });
10780                 return;
10781             }
10782         }
10783         
10784         var days = date.getDaysInMonth();
10785         var firstOfMonth = date.getFirstDateOfMonth();
10786         var startingPos = firstOfMonth.getDay()-this.startDay;
10787
10788         if(startingPos <= this.startDay){
10789             startingPos += 7;
10790         }
10791
10792         var pm = date.add("mo", -1);
10793         var prevStart = pm.getDaysInMonth()-startingPos;
10794
10795         var cells = this.cells.elements;
10796         var textEls = this.textNodes;
10797         days += startingPos;
10798
10799         // convert everything to numbers so it's fast
10800         var day = 86400000;
10801         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10802         var today = new Date().clearTime().getTime();
10803         var sel = date.clearTime().getTime();
10804         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10805         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10806         var ddMatch = this.disabledDatesRE;
10807         var ddText = this.disabledDatesText;
10808         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10809         var ddaysText = this.disabledDaysText;
10810         var format = this.format;
10811
10812         var setCellClass = function(cal, cell){
10813             cell.title = "";
10814             var t = d.getTime();
10815             cell.firstChild.dateValue = t;
10816             if(t == today){
10817                 cell.className += " x-date-today";
10818                 cell.title = cal.todayText;
10819             }
10820             if(t == sel){
10821                 cell.className += " x-date-selected";
10822                 setTimeout(function(){
10823                     try{cell.firstChild.focus();}catch(e){}
10824                 }, 50);
10825             }
10826             // disabling
10827             if(t < min) {
10828                 cell.className = " x-date-disabled";
10829                 cell.title = cal.minText;
10830                 return;
10831             }
10832             if(t > max) {
10833                 cell.className = " x-date-disabled";
10834                 cell.title = cal.maxText;
10835                 return;
10836             }
10837             if(ddays){
10838                 if(ddays.indexOf(d.getDay()) != -1){
10839                     cell.title = ddaysText;
10840                     cell.className = " x-date-disabled";
10841                 }
10842             }
10843             if(ddMatch && format){
10844                 var fvalue = d.dateFormat(format);
10845                 if(ddMatch.test(fvalue)){
10846                     cell.title = ddText.replace("%0", fvalue);
10847                     cell.className = " x-date-disabled";
10848                 }
10849             }
10850         };
10851
10852         var i = 0;
10853         for(; i < startingPos; i++) {
10854             textEls[i].innerHTML = (++prevStart);
10855             d.setDate(d.getDate()+1);
10856             cells[i].className = "x-date-prevday";
10857             setCellClass(this, cells[i]);
10858         }
10859         for(; i < days; i++){
10860             intDay = i - startingPos + 1;
10861             textEls[i].innerHTML = (intDay);
10862             d.setDate(d.getDate()+1);
10863             cells[i].className = "x-date-active";
10864             setCellClass(this, cells[i]);
10865         }
10866         var extraDays = 0;
10867         for(; i < 42; i++) {
10868              textEls[i].innerHTML = (++extraDays);
10869              d.setDate(d.getDate()+1);
10870              cells[i].className = "x-date-nextday";
10871              setCellClass(this, cells[i]);
10872         }
10873
10874         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10875         this.fireEvent('monthchange', this, date);
10876         
10877         if(!this.internalRender){
10878             var main = this.el.dom.firstChild;
10879             var w = main.offsetWidth;
10880             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10881             Roo.fly(main).setWidth(w);
10882             this.internalRender = true;
10883             // opera does not respect the auto grow header center column
10884             // then, after it gets a width opera refuses to recalculate
10885             // without a second pass
10886             if(Roo.isOpera && !this.secondPass){
10887                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10888                 this.secondPass = true;
10889                 this.update.defer(10, this, [date]);
10890             }
10891         }
10892         
10893         
10894     }
10895 });        /*
10896  * Based on:
10897  * Ext JS Library 1.1.1
10898  * Copyright(c) 2006-2007, Ext JS, LLC.
10899  *
10900  * Originally Released Under LGPL - original licence link has changed is not relivant.
10901  *
10902  * Fork - LGPL
10903  * <script type="text/javascript">
10904  */
10905 /**
10906  * @class Roo.TabPanel
10907  * @extends Roo.util.Observable
10908  * A lightweight tab container.
10909  * <br><br>
10910  * Usage:
10911  * <pre><code>
10912 // basic tabs 1, built from existing content
10913 var tabs = new Roo.TabPanel("tabs1");
10914 tabs.addTab("script", "View Script");
10915 tabs.addTab("markup", "View Markup");
10916 tabs.activate("script");
10917
10918 // more advanced tabs, built from javascript
10919 var jtabs = new Roo.TabPanel("jtabs");
10920 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10921
10922 // set up the UpdateManager
10923 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10924 var updater = tab2.getUpdateManager();
10925 updater.setDefaultUrl("ajax1.htm");
10926 tab2.on('activate', updater.refresh, updater, true);
10927
10928 // Use setUrl for Ajax loading
10929 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10930 tab3.setUrl("ajax2.htm", null, true);
10931
10932 // Disabled tab
10933 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10934 tab4.disable();
10935
10936 jtabs.activate("jtabs-1");
10937  * </code></pre>
10938  * @constructor
10939  * Create a new TabPanel.
10940  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10941  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10942  */
10943 Roo.TabPanel = function(container, config){
10944     /**
10945     * The container element for this TabPanel.
10946     * @type Roo.Element
10947     */
10948     this.el = Roo.get(container, true);
10949     if(config){
10950         if(typeof config == "boolean"){
10951             this.tabPosition = config ? "bottom" : "top";
10952         }else{
10953             Roo.apply(this, config);
10954         }
10955     }
10956     if(this.tabPosition == "bottom"){
10957         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10958         this.el.addClass("x-tabs-bottom");
10959     }
10960     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10961     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10962     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10963     if(Roo.isIE){
10964         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10965     }
10966     if(this.tabPosition != "bottom"){
10967         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
10968          * @type Roo.Element
10969          */
10970         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10971         this.el.addClass("x-tabs-top");
10972     }
10973     this.items = [];
10974
10975     this.bodyEl.setStyle("position", "relative");
10976
10977     this.active = null;
10978     this.activateDelegate = this.activate.createDelegate(this);
10979
10980     this.addEvents({
10981         /**
10982          * @event tabchange
10983          * Fires when the active tab changes
10984          * @param {Roo.TabPanel} this
10985          * @param {Roo.TabPanelItem} activePanel The new active tab
10986          */
10987         "tabchange": true,
10988         /**
10989          * @event beforetabchange
10990          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
10991          * @param {Roo.TabPanel} this
10992          * @param {Object} e Set cancel to true on this object to cancel the tab change
10993          * @param {Roo.TabPanelItem} tab The tab being changed to
10994          */
10995         "beforetabchange" : true
10996     });
10997
10998     Roo.EventManager.onWindowResize(this.onResize, this);
10999     this.cpad = this.el.getPadding("lr");
11000     this.hiddenCount = 0;
11001
11002
11003     // toolbar on the tabbar support...
11004     if (this.toolbar) {
11005         var tcfg = this.toolbar;
11006         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
11007         this.toolbar = new Roo.Toolbar(tcfg);
11008         if (Roo.isSafari) {
11009             var tbl = tcfg.container.child('table', true);
11010             tbl.setAttribute('width', '100%');
11011         }
11012         
11013     }
11014    
11015
11016
11017     Roo.TabPanel.superclass.constructor.call(this);
11018 };
11019
11020 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
11021     /*
11022      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
11023      */
11024     tabPosition : "top",
11025     /*
11026      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
11027      */
11028     currentTabWidth : 0,
11029     /*
11030      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
11031      */
11032     minTabWidth : 40,
11033     /*
11034      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
11035      */
11036     maxTabWidth : 250,
11037     /*
11038      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
11039      */
11040     preferredTabWidth : 175,
11041     /*
11042      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
11043      */
11044     resizeTabs : false,
11045     /*
11046      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
11047      */
11048     monitorResize : true,
11049     /*
11050      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
11051      */
11052     toolbar : false,
11053
11054     /**
11055      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
11056      * @param {String} id The id of the div to use <b>or create</b>
11057      * @param {String} text The text for the tab
11058      * @param {String} content (optional) Content to put in the TabPanelItem body
11059      * @param {Boolean} closable (optional) True to create a close icon on the tab
11060      * @return {Roo.TabPanelItem} The created TabPanelItem
11061      */
11062     addTab : function(id, text, content, closable){
11063         var item = new Roo.TabPanelItem(this, id, text, closable);
11064         this.addTabItem(item);
11065         if(content){
11066             item.setContent(content);
11067         }
11068         return item;
11069     },
11070
11071     /**
11072      * Returns the {@link Roo.TabPanelItem} with the specified id/index
11073      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
11074      * @return {Roo.TabPanelItem}
11075      */
11076     getTab : function(id){
11077         return this.items[id];
11078     },
11079
11080     /**
11081      * Hides the {@link Roo.TabPanelItem} with the specified id/index
11082      * @param {String/Number} id The id or index of the TabPanelItem to hide.
11083      */
11084     hideTab : function(id){
11085         var t = this.items[id];
11086         if(!t.isHidden()){
11087            t.setHidden(true);
11088            this.hiddenCount++;
11089            this.autoSizeTabs();
11090         }
11091     },
11092
11093     /**
11094      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
11095      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
11096      */
11097     unhideTab : function(id){
11098         var t = this.items[id];
11099         if(t.isHidden()){
11100            t.setHidden(false);
11101            this.hiddenCount--;
11102            this.autoSizeTabs();
11103         }
11104     },
11105
11106     /**
11107      * Adds an existing {@link Roo.TabPanelItem}.
11108      * @param {Roo.TabPanelItem} item The TabPanelItem to add
11109      */
11110     addTabItem : function(item){
11111         this.items[item.id] = item;
11112         this.items.push(item);
11113         if(this.resizeTabs){
11114            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
11115            this.autoSizeTabs();
11116         }else{
11117             item.autoSize();
11118         }
11119     },
11120
11121     /**
11122      * Removes a {@link Roo.TabPanelItem}.
11123      * @param {String/Number} id The id or index of the TabPanelItem to remove.
11124      */
11125     removeTab : function(id){
11126         var items = this.items;
11127         var tab = items[id];
11128         if(!tab) { return; }
11129         var index = items.indexOf(tab);
11130         if(this.active == tab && items.length > 1){
11131             var newTab = this.getNextAvailable(index);
11132             if(newTab) {
11133                 newTab.activate();
11134             }
11135         }
11136         this.stripEl.dom.removeChild(tab.pnode.dom);
11137         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
11138             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
11139         }
11140         items.splice(index, 1);
11141         delete this.items[tab.id];
11142         tab.fireEvent("close", tab);
11143         tab.purgeListeners();
11144         this.autoSizeTabs();
11145     },
11146
11147     getNextAvailable : function(start){
11148         var items = this.items;
11149         var index = start;
11150         // look for a next tab that will slide over to
11151         // replace the one being removed
11152         while(index < items.length){
11153             var item = items[++index];
11154             if(item && !item.isHidden()){
11155                 return item;
11156             }
11157         }
11158         // if one isn't found select the previous tab (on the left)
11159         index = start;
11160         while(index >= 0){
11161             var item = items[--index];
11162             if(item && !item.isHidden()){
11163                 return item;
11164             }
11165         }
11166         return null;
11167     },
11168
11169     /**
11170      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
11171      * @param {String/Number} id The id or index of the TabPanelItem to disable.
11172      */
11173     disableTab : function(id){
11174         var tab = this.items[id];
11175         if(tab && this.active != tab){
11176             tab.disable();
11177         }
11178     },
11179
11180     /**
11181      * Enables a {@link Roo.TabPanelItem} that is disabled.
11182      * @param {String/Number} id The id or index of the TabPanelItem to enable.
11183      */
11184     enableTab : function(id){
11185         var tab = this.items[id];
11186         tab.enable();
11187     },
11188
11189     /**
11190      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
11191      * @param {String/Number} id The id or index of the TabPanelItem to activate.
11192      * @return {Roo.TabPanelItem} The TabPanelItem.
11193      */
11194     activate : function(id){
11195         var tab = this.items[id];
11196         if(!tab){
11197             return null;
11198         }
11199         if(tab == this.active || tab.disabled){
11200             return tab;
11201         }
11202         var e = {};
11203         this.fireEvent("beforetabchange", this, e, tab);
11204         if(e.cancel !== true && !tab.disabled){
11205             if(this.active){
11206                 this.active.hide();
11207             }
11208             this.active = this.items[id];
11209             this.active.show();
11210             this.fireEvent("tabchange", this, this.active);
11211         }
11212         return tab;
11213     },
11214
11215     /**
11216      * Gets the active {@link Roo.TabPanelItem}.
11217      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
11218      */
11219     getActiveTab : function(){
11220         return this.active;
11221     },
11222
11223     /**
11224      * Updates the tab body element to fit the height of the container element
11225      * for overflow scrolling
11226      * @param {Number} targetHeight (optional) Override the starting height from the elements height
11227      */
11228     syncHeight : function(targetHeight){
11229         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
11230         var bm = this.bodyEl.getMargins();
11231         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
11232         this.bodyEl.setHeight(newHeight);
11233         return newHeight;
11234     },
11235
11236     onResize : function(){
11237         if(this.monitorResize){
11238             this.autoSizeTabs();
11239         }
11240     },
11241
11242     /**
11243      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
11244      */
11245     beginUpdate : function(){
11246         this.updating = true;
11247     },
11248
11249     /**
11250      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
11251      */
11252     endUpdate : function(){
11253         this.updating = false;
11254         this.autoSizeTabs();
11255     },
11256
11257     /**
11258      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
11259      */
11260     autoSizeTabs : function(){
11261         var count = this.items.length;
11262         var vcount = count - this.hiddenCount;
11263         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
11264         var w = Math.max(this.el.getWidth() - this.cpad, 10);
11265         var availWidth = Math.floor(w / vcount);
11266         var b = this.stripBody;
11267         if(b.getWidth() > w){
11268             var tabs = this.items;
11269             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
11270             if(availWidth < this.minTabWidth){
11271                 /*if(!this.sleft){    // incomplete scrolling code
11272                     this.createScrollButtons();
11273                 }
11274                 this.showScroll();
11275                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
11276             }
11277         }else{
11278             if(this.currentTabWidth < this.preferredTabWidth){
11279                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
11280             }
11281         }
11282     },
11283
11284     /**
11285      * Returns the number of tabs in this TabPanel.
11286      * @return {Number}
11287      */
11288      getCount : function(){
11289          return this.items.length;
11290      },
11291
11292     /**
11293      * Resizes all the tabs to the passed width
11294      * @param {Number} The new width
11295      */
11296     setTabWidth : function(width){
11297         this.currentTabWidth = width;
11298         for(var i = 0, len = this.items.length; i < len; i++) {
11299                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
11300         }
11301     },
11302
11303     /**
11304      * Destroys this TabPanel
11305      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
11306      */
11307     destroy : function(removeEl){
11308         Roo.EventManager.removeResizeListener(this.onResize, this);
11309         for(var i = 0, len = this.items.length; i < len; i++){
11310             this.items[i].purgeListeners();
11311         }
11312         if(removeEl === true){
11313             this.el.update("");
11314             this.el.remove();
11315         }
11316     }
11317 });
11318
11319 /**
11320  * @class Roo.TabPanelItem
11321  * @extends Roo.util.Observable
11322  * Represents an individual item (tab plus body) in a TabPanel.
11323  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
11324  * @param {String} id The id of this TabPanelItem
11325  * @param {String} text The text for the tab of this TabPanelItem
11326  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
11327  */
11328 Roo.TabPanelItem = function(tabPanel, id, text, closable){
11329     /**
11330      * The {@link Roo.TabPanel} this TabPanelItem belongs to
11331      * @type Roo.TabPanel
11332      */
11333     this.tabPanel = tabPanel;
11334     /**
11335      * The id for this TabPanelItem
11336      * @type String
11337      */
11338     this.id = id;
11339     /** @private */
11340     this.disabled = false;
11341     /** @private */
11342     this.text = text;
11343     /** @private */
11344     this.loaded = false;
11345     this.closable = closable;
11346
11347     /**
11348      * The body element for this TabPanelItem.
11349      * @type Roo.Element
11350      */
11351     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
11352     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
11353     this.bodyEl.setStyle("display", "block");
11354     this.bodyEl.setStyle("zoom", "1");
11355     this.hideAction();
11356
11357     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
11358     /** @private */
11359     this.el = Roo.get(els.el, true);
11360     this.inner = Roo.get(els.inner, true);
11361     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
11362     this.pnode = Roo.get(els.el.parentNode, true);
11363     this.el.on("mousedown", this.onTabMouseDown, this);
11364     this.el.on("click", this.onTabClick, this);
11365     /** @private */
11366     if(closable){
11367         var c = Roo.get(els.close, true);
11368         c.dom.title = this.closeText;
11369         c.addClassOnOver("close-over");
11370         c.on("click", this.closeClick, this);
11371      }
11372
11373     this.addEvents({
11374          /**
11375          * @event activate
11376          * Fires when this tab becomes the active tab.
11377          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11378          * @param {Roo.TabPanelItem} this
11379          */
11380         "activate": true,
11381         /**
11382          * @event beforeclose
11383          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
11384          * @param {Roo.TabPanelItem} this
11385          * @param {Object} e Set cancel to true on this object to cancel the close.
11386          */
11387         "beforeclose": true,
11388         /**
11389          * @event close
11390          * Fires when this tab is closed.
11391          * @param {Roo.TabPanelItem} this
11392          */
11393          "close": true,
11394         /**
11395          * @event deactivate
11396          * Fires when this tab is no longer the active tab.
11397          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11398          * @param {Roo.TabPanelItem} this
11399          */
11400          "deactivate" : true
11401     });
11402     this.hidden = false;
11403
11404     Roo.TabPanelItem.superclass.constructor.call(this);
11405 };
11406
11407 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
11408     purgeListeners : function(){
11409        Roo.util.Observable.prototype.purgeListeners.call(this);
11410        this.el.removeAllListeners();
11411     },
11412     /**
11413      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
11414      */
11415     show : function(){
11416         this.pnode.addClass("on");
11417         this.showAction();
11418         if(Roo.isOpera){
11419             this.tabPanel.stripWrap.repaint();
11420         }
11421         this.fireEvent("activate", this.tabPanel, this);
11422     },
11423
11424     /**
11425      * Returns true if this tab is the active tab.
11426      * @return {Boolean}
11427      */
11428     isActive : function(){
11429         return this.tabPanel.getActiveTab() == this;
11430     },
11431
11432     /**
11433      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
11434      */
11435     hide : function(){
11436         this.pnode.removeClass("on");
11437         this.hideAction();
11438         this.fireEvent("deactivate", this.tabPanel, this);
11439     },
11440
11441     hideAction : function(){
11442         this.bodyEl.hide();
11443         this.bodyEl.setStyle("position", "absolute");
11444         this.bodyEl.setLeft("-20000px");
11445         this.bodyEl.setTop("-20000px");
11446     },
11447
11448     showAction : function(){
11449         this.bodyEl.setStyle("position", "relative");
11450         this.bodyEl.setTop("");
11451         this.bodyEl.setLeft("");
11452         this.bodyEl.show();
11453     },
11454
11455     /**
11456      * Set the tooltip for the tab.
11457      * @param {String} tooltip The tab's tooltip
11458      */
11459     setTooltip : function(text){
11460         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
11461             this.textEl.dom.qtip = text;
11462             this.textEl.dom.removeAttribute('title');
11463         }else{
11464             this.textEl.dom.title = text;
11465         }
11466     },
11467
11468     onTabClick : function(e){
11469         e.preventDefault();
11470         this.tabPanel.activate(this.id);
11471     },
11472
11473     onTabMouseDown : function(e){
11474         e.preventDefault();
11475         this.tabPanel.activate(this.id);
11476     },
11477
11478     getWidth : function(){
11479         return this.inner.getWidth();
11480     },
11481
11482     setWidth : function(width){
11483         var iwidth = width - this.pnode.getPadding("lr");
11484         this.inner.setWidth(iwidth);
11485         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
11486         this.pnode.setWidth(width);
11487     },
11488
11489     /**
11490      * Show or hide the tab
11491      * @param {Boolean} hidden True to hide or false to show.
11492      */
11493     setHidden : function(hidden){
11494         this.hidden = hidden;
11495         this.pnode.setStyle("display", hidden ? "none" : "");
11496     },
11497
11498     /**
11499      * Returns true if this tab is "hidden"
11500      * @return {Boolean}
11501      */
11502     isHidden : function(){
11503         return this.hidden;
11504     },
11505
11506     /**
11507      * Returns the text for this tab
11508      * @return {String}
11509      */
11510     getText : function(){
11511         return this.text;
11512     },
11513
11514     autoSize : function(){
11515         //this.el.beginMeasure();
11516         this.textEl.setWidth(1);
11517         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
11518         //this.el.endMeasure();
11519     },
11520
11521     /**
11522      * Sets the text for the tab (Note: this also sets the tooltip text)
11523      * @param {String} text The tab's text and tooltip
11524      */
11525     setText : function(text){
11526         this.text = text;
11527         this.textEl.update(text);
11528         this.setTooltip(text);
11529         if(!this.tabPanel.resizeTabs){
11530             this.autoSize();
11531         }
11532     },
11533     /**
11534      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
11535      */
11536     activate : function(){
11537         this.tabPanel.activate(this.id);
11538     },
11539
11540     /**
11541      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
11542      */
11543     disable : function(){
11544         if(this.tabPanel.active != this){
11545             this.disabled = true;
11546             this.pnode.addClass("disabled");
11547         }
11548     },
11549
11550     /**
11551      * Enables this TabPanelItem if it was previously disabled.
11552      */
11553     enable : function(){
11554         this.disabled = false;
11555         this.pnode.removeClass("disabled");
11556     },
11557
11558     /**
11559      * Sets the content for this TabPanelItem.
11560      * @param {String} content The content
11561      * @param {Boolean} loadScripts true to look for and load scripts
11562      */
11563     setContent : function(content, loadScripts){
11564         this.bodyEl.update(content, loadScripts);
11565     },
11566
11567     /**
11568      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
11569      * @return {Roo.UpdateManager} The UpdateManager
11570      */
11571     getUpdateManager : function(){
11572         return this.bodyEl.getUpdateManager();
11573     },
11574
11575     /**
11576      * Set a URL to be used to load the content for this TabPanelItem.
11577      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
11578      * @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)
11579      * @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)
11580      * @return {Roo.UpdateManager} The UpdateManager
11581      */
11582     setUrl : function(url, params, loadOnce){
11583         if(this.refreshDelegate){
11584             this.un('activate', this.refreshDelegate);
11585         }
11586         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
11587         this.on("activate", this.refreshDelegate);
11588         return this.bodyEl.getUpdateManager();
11589     },
11590
11591     /** @private */
11592     _handleRefresh : function(url, params, loadOnce){
11593         if(!loadOnce || !this.loaded){
11594             var updater = this.bodyEl.getUpdateManager();
11595             updater.update(url, params, this._setLoaded.createDelegate(this));
11596         }
11597     },
11598
11599     /**
11600      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
11601      *   Will fail silently if the setUrl method has not been called.
11602      *   This does not activate the panel, just updates its content.
11603      */
11604     refresh : function(){
11605         if(this.refreshDelegate){
11606            this.loaded = false;
11607            this.refreshDelegate();
11608         }
11609     },
11610
11611     /** @private */
11612     _setLoaded : function(){
11613         this.loaded = true;
11614     },
11615
11616     /** @private */
11617     closeClick : function(e){
11618         var o = {};
11619         e.stopEvent();
11620         this.fireEvent("beforeclose", this, o);
11621         if(o.cancel !== true){
11622             this.tabPanel.removeTab(this.id);
11623         }
11624     },
11625     /**
11626      * The text displayed in the tooltip for the close icon.
11627      * @type String
11628      */
11629     closeText : "Close this tab"
11630 });
11631
11632 /** @private */
11633 Roo.TabPanel.prototype.createStrip = function(container){
11634     var strip = document.createElement("div");
11635     strip.className = "x-tabs-wrap";
11636     container.appendChild(strip);
11637     return strip;
11638 };
11639 /** @private */
11640 Roo.TabPanel.prototype.createStripList = function(strip){
11641     // div wrapper for retard IE
11642     // returns the "tr" element.
11643     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
11644         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
11645         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
11646     return strip.firstChild.firstChild.firstChild.firstChild;
11647 };
11648 /** @private */
11649 Roo.TabPanel.prototype.createBody = function(container){
11650     var body = document.createElement("div");
11651     Roo.id(body, "tab-body");
11652     Roo.fly(body).addClass("x-tabs-body");
11653     container.appendChild(body);
11654     return body;
11655 };
11656 /** @private */
11657 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
11658     var body = Roo.getDom(id);
11659     if(!body){
11660         body = document.createElement("div");
11661         body.id = id;
11662     }
11663     Roo.fly(body).addClass("x-tabs-item-body");
11664     bodyEl.insertBefore(body, bodyEl.firstChild);
11665     return body;
11666 };
11667 /** @private */
11668 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
11669     var td = document.createElement("td");
11670     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
11671     //stripEl.appendChild(td);
11672     if(closable){
11673         td.className = "x-tabs-closable";
11674         if(!this.closeTpl){
11675             this.closeTpl = new Roo.Template(
11676                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11677                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
11678                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
11679             );
11680         }
11681         var el = this.closeTpl.overwrite(td, {"text": text});
11682         var close = el.getElementsByTagName("div")[0];
11683         var inner = el.getElementsByTagName("em")[0];
11684         return {"el": el, "close": close, "inner": inner};
11685     } else {
11686         if(!this.tabTpl){
11687             this.tabTpl = new Roo.Template(
11688                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11689                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
11690             );
11691         }
11692         var el = this.tabTpl.overwrite(td, {"text": text});
11693         var inner = el.getElementsByTagName("em")[0];
11694         return {"el": el, "inner": inner};
11695     }
11696 };/*
11697  * Based on:
11698  * Ext JS Library 1.1.1
11699  * Copyright(c) 2006-2007, Ext JS, LLC.
11700  *
11701  * Originally Released Under LGPL - original licence link has changed is not relivant.
11702  *
11703  * Fork - LGPL
11704  * <script type="text/javascript">
11705  */
11706
11707 /**
11708  * @class Roo.Button
11709  * @extends Roo.util.Observable
11710  * Simple Button class
11711  * @cfg {String} text The button text
11712  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11713  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11714  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11715  * @cfg {Object} scope The scope of the handler
11716  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11717  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11718  * @cfg {Boolean} hidden True to start hidden (defaults to false)
11719  * @cfg {Boolean} disabled True to start disabled (defaults to false)
11720  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11721  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11722    applies if enableToggle = true)
11723  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11724  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11725   an {@link Roo.util.ClickRepeater} config object (defaults to false).
11726  * @constructor
11727  * Create a new button
11728  * @param {Object} config The config object
11729  */
11730 Roo.Button = function(renderTo, config)
11731 {
11732     if (!config) {
11733         config = renderTo;
11734         renderTo = config.renderTo || false;
11735     }
11736     
11737     Roo.apply(this, config);
11738     this.addEvents({
11739         /**
11740              * @event click
11741              * Fires when this button is clicked
11742              * @param {Button} this
11743              * @param {EventObject} e The click event
11744              */
11745             "click" : true,
11746         /**
11747              * @event toggle
11748              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11749              * @param {Button} this
11750              * @param {Boolean} pressed
11751              */
11752             "toggle" : true,
11753         /**
11754              * @event mouseover
11755              * Fires when the mouse hovers over the button
11756              * @param {Button} this
11757              * @param {Event} e The event object
11758              */
11759         'mouseover' : true,
11760         /**
11761              * @event mouseout
11762              * Fires when the mouse exits the button
11763              * @param {Button} this
11764              * @param {Event} e The event object
11765              */
11766         'mouseout': true,
11767          /**
11768              * @event render
11769              * Fires when the button is rendered
11770              * @param {Button} this
11771              */
11772         'render': true
11773     });
11774     if(this.menu){
11775         this.menu = Roo.menu.MenuMgr.get(this.menu);
11776     }
11777     // register listeners first!!  - so render can be captured..
11778     Roo.util.Observable.call(this);
11779     if(renderTo){
11780         this.render(renderTo);
11781     }
11782     
11783   
11784 };
11785
11786 Roo.extend(Roo.Button, Roo.util.Observable, {
11787     /**
11788      * 
11789      */
11790     
11791     /**
11792      * Read-only. True if this button is hidden
11793      * @type Boolean
11794      */
11795     hidden : false,
11796     /**
11797      * Read-only. True if this button is disabled
11798      * @type Boolean
11799      */
11800     disabled : false,
11801     /**
11802      * Read-only. True if this button is pressed (only if enableToggle = true)
11803      * @type Boolean
11804      */
11805     pressed : false,
11806
11807     /**
11808      * @cfg {Number} tabIndex 
11809      * The DOM tabIndex for this button (defaults to undefined)
11810      */
11811     tabIndex : undefined,
11812
11813     /**
11814      * @cfg {Boolean} enableToggle
11815      * True to enable pressed/not pressed toggling (defaults to false)
11816      */
11817     enableToggle: false,
11818     /**
11819      * @cfg {Mixed} menu
11820      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11821      */
11822     menu : undefined,
11823     /**
11824      * @cfg {String} menuAlign
11825      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11826      */
11827     menuAlign : "tl-bl?",
11828
11829     /**
11830      * @cfg {String} iconCls
11831      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11832      */
11833     iconCls : undefined,
11834     /**
11835      * @cfg {String} type
11836      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
11837      */
11838     type : 'button',
11839
11840     // private
11841     menuClassTarget: 'tr',
11842
11843     /**
11844      * @cfg {String} clickEvent
11845      * The type of event to map to the button's event handler (defaults to 'click')
11846      */
11847     clickEvent : 'click',
11848
11849     /**
11850      * @cfg {Boolean} handleMouseEvents
11851      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11852      */
11853     handleMouseEvents : true,
11854
11855     /**
11856      * @cfg {String} tooltipType
11857      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11858      */
11859     tooltipType : 'qtip',
11860
11861     /**
11862      * @cfg {String} cls
11863      * A CSS class to apply to the button's main element.
11864      */
11865     
11866     /**
11867      * @cfg {Roo.Template} template (Optional)
11868      * An {@link Roo.Template} with which to create the Button's main element. This Template must
11869      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11870      * require code modifications if required elements (e.g. a button) aren't present.
11871      */
11872
11873     // private
11874     render : function(renderTo){
11875         var btn;
11876         if(this.hideParent){
11877             this.parentEl = Roo.get(renderTo);
11878         }
11879         if(!this.dhconfig){
11880             if(!this.template){
11881                 if(!Roo.Button.buttonTemplate){
11882                     // hideous table template
11883                     Roo.Button.buttonTemplate = new Roo.Template(
11884                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11885                         '<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>',
11886                         "</tr></tbody></table>");
11887                 }
11888                 this.template = Roo.Button.buttonTemplate;
11889             }
11890             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
11891             var btnEl = btn.child("button:first");
11892             btnEl.on('focus', this.onFocus, this);
11893             btnEl.on('blur', this.onBlur, this);
11894             if(this.cls){
11895                 btn.addClass(this.cls);
11896             }
11897             if(this.icon){
11898                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11899             }
11900             if(this.iconCls){
11901                 btnEl.addClass(this.iconCls);
11902                 if(!this.cls){
11903                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11904                 }
11905             }
11906             if(this.tabIndex !== undefined){
11907                 btnEl.dom.tabIndex = this.tabIndex;
11908             }
11909             if(this.tooltip){
11910                 if(typeof this.tooltip == 'object'){
11911                     Roo.QuickTips.tips(Roo.apply({
11912                           target: btnEl.id
11913                     }, this.tooltip));
11914                 } else {
11915                     btnEl.dom[this.tooltipType] = this.tooltip;
11916                 }
11917             }
11918         }else{
11919             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11920         }
11921         this.el = btn;
11922         if(this.id){
11923             this.el.dom.id = this.el.id = this.id;
11924         }
11925         if(this.menu){
11926             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11927             this.menu.on("show", this.onMenuShow, this);
11928             this.menu.on("hide", this.onMenuHide, this);
11929         }
11930         btn.addClass("x-btn");
11931         if(Roo.isIE && !Roo.isIE7){
11932             this.autoWidth.defer(1, this);
11933         }else{
11934             this.autoWidth();
11935         }
11936         if(this.handleMouseEvents){
11937             btn.on("mouseover", this.onMouseOver, this);
11938             btn.on("mouseout", this.onMouseOut, this);
11939             btn.on("mousedown", this.onMouseDown, this);
11940         }
11941         btn.on(this.clickEvent, this.onClick, this);
11942         //btn.on("mouseup", this.onMouseUp, this);
11943         if(this.hidden){
11944             this.hide();
11945         }
11946         if(this.disabled){
11947             this.disable();
11948         }
11949         Roo.ButtonToggleMgr.register(this);
11950         if(this.pressed){
11951             this.el.addClass("x-btn-pressed");
11952         }
11953         if(this.repeat){
11954             var repeater = new Roo.util.ClickRepeater(btn,
11955                 typeof this.repeat == "object" ? this.repeat : {}
11956             );
11957             repeater.on("click", this.onClick,  this);
11958         }
11959         
11960         this.fireEvent('render', this);
11961         
11962     },
11963     /**
11964      * Returns the button's underlying element
11965      * @return {Roo.Element} The element
11966      */
11967     getEl : function(){
11968         return this.el;  
11969     },
11970     
11971     /**
11972      * Destroys this Button and removes any listeners.
11973      */
11974     destroy : function(){
11975         Roo.ButtonToggleMgr.unregister(this);
11976         this.el.removeAllListeners();
11977         this.purgeListeners();
11978         this.el.remove();
11979     },
11980
11981     // private
11982     autoWidth : function(){
11983         if(this.el){
11984             this.el.setWidth("auto");
11985             if(Roo.isIE7 && Roo.isStrict){
11986                 var ib = this.el.child('button');
11987                 if(ib && ib.getWidth() > 20){
11988                     ib.clip();
11989                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11990                 }
11991             }
11992             if(this.minWidth){
11993                 if(this.hidden){
11994                     this.el.beginMeasure();
11995                 }
11996                 if(this.el.getWidth() < this.minWidth){
11997                     this.el.setWidth(this.minWidth);
11998                 }
11999                 if(this.hidden){
12000                     this.el.endMeasure();
12001                 }
12002             }
12003         }
12004     },
12005
12006     /**
12007      * Assigns this button's click handler
12008      * @param {Function} handler The function to call when the button is clicked
12009      * @param {Object} scope (optional) Scope for the function passed in
12010      */
12011     setHandler : function(handler, scope){
12012         this.handler = handler;
12013         this.scope = scope;  
12014     },
12015     
12016     /**
12017      * Sets this button's text
12018      * @param {String} text The button text
12019      */
12020     setText : function(text){
12021         this.text = text;
12022         if(this.el){
12023             this.el.child("td.x-btn-center button.x-btn-text").update(text);
12024         }
12025         this.autoWidth();
12026     },
12027     
12028     /**
12029      * Gets the text for this button
12030      * @return {String} The button text
12031      */
12032     getText : function(){
12033         return this.text;  
12034     },
12035     
12036     /**
12037      * Show this button
12038      */
12039     show: function(){
12040         this.hidden = false;
12041         if(this.el){
12042             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
12043         }
12044     },
12045     
12046     /**
12047      * Hide this button
12048      */
12049     hide: function(){
12050         this.hidden = true;
12051         if(this.el){
12052             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
12053         }
12054     },
12055     
12056     /**
12057      * Convenience function for boolean show/hide
12058      * @param {Boolean} visible True to show, false to hide
12059      */
12060     setVisible: function(visible){
12061         if(visible) {
12062             this.show();
12063         }else{
12064             this.hide();
12065         }
12066     },
12067     
12068     /**
12069      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
12070      * @param {Boolean} state (optional) Force a particular state
12071      */
12072     toggle : function(state){
12073         state = state === undefined ? !this.pressed : state;
12074         if(state != this.pressed){
12075             if(state){
12076                 this.el.addClass("x-btn-pressed");
12077                 this.pressed = true;
12078                 this.fireEvent("toggle", this, true);
12079             }else{
12080                 this.el.removeClass("x-btn-pressed");
12081                 this.pressed = false;
12082                 this.fireEvent("toggle", this, false);
12083             }
12084             if(this.toggleHandler){
12085                 this.toggleHandler.call(this.scope || this, this, state);
12086             }
12087         }
12088     },
12089     
12090     /**
12091      * Focus the button
12092      */
12093     focus : function(){
12094         this.el.child('button:first').focus();
12095     },
12096     
12097     /**
12098      * Disable this button
12099      */
12100     disable : function(){
12101         if(this.el){
12102             this.el.addClass("x-btn-disabled");
12103         }
12104         this.disabled = true;
12105     },
12106     
12107     /**
12108      * Enable this button
12109      */
12110     enable : function(){
12111         if(this.el){
12112             this.el.removeClass("x-btn-disabled");
12113         }
12114         this.disabled = false;
12115     },
12116
12117     /**
12118      * Convenience function for boolean enable/disable
12119      * @param {Boolean} enabled True to enable, false to disable
12120      */
12121     setDisabled : function(v){
12122         this[v !== true ? "enable" : "disable"]();
12123     },
12124
12125     // private
12126     onClick : function(e){
12127         if(e){
12128             e.preventDefault();
12129         }
12130         if(e.button != 0){
12131             return;
12132         }
12133         if(!this.disabled){
12134             if(this.enableToggle){
12135                 this.toggle();
12136             }
12137             if(this.menu && !this.menu.isVisible()){
12138                 this.menu.show(this.el, this.menuAlign);
12139             }
12140             this.fireEvent("click", this, e);
12141             if(this.handler){
12142                 this.el.removeClass("x-btn-over");
12143                 this.handler.call(this.scope || this, this, e);
12144             }
12145         }
12146     },
12147     // private
12148     onMouseOver : function(e){
12149         if(!this.disabled){
12150             this.el.addClass("x-btn-over");
12151             this.fireEvent('mouseover', this, e);
12152         }
12153     },
12154     // private
12155     onMouseOut : function(e){
12156         if(!e.within(this.el,  true)){
12157             this.el.removeClass("x-btn-over");
12158             this.fireEvent('mouseout', this, e);
12159         }
12160     },
12161     // private
12162     onFocus : function(e){
12163         if(!this.disabled){
12164             this.el.addClass("x-btn-focus");
12165         }
12166     },
12167     // private
12168     onBlur : function(e){
12169         this.el.removeClass("x-btn-focus");
12170     },
12171     // private
12172     onMouseDown : function(e){
12173         if(!this.disabled && e.button == 0){
12174             this.el.addClass("x-btn-click");
12175             Roo.get(document).on('mouseup', this.onMouseUp, this);
12176         }
12177     },
12178     // private
12179     onMouseUp : function(e){
12180         if(e.button == 0){
12181             this.el.removeClass("x-btn-click");
12182             Roo.get(document).un('mouseup', this.onMouseUp, this);
12183         }
12184     },
12185     // private
12186     onMenuShow : function(e){
12187         this.el.addClass("x-btn-menu-active");
12188     },
12189     // private
12190     onMenuHide : function(e){
12191         this.el.removeClass("x-btn-menu-active");
12192     }   
12193 });
12194
12195 // Private utility class used by Button
12196 Roo.ButtonToggleMgr = function(){
12197    var groups = {};
12198    
12199    function toggleGroup(btn, state){
12200        if(state){
12201            var g = groups[btn.toggleGroup];
12202            for(var i = 0, l = g.length; i < l; i++){
12203                if(g[i] != btn){
12204                    g[i].toggle(false);
12205                }
12206            }
12207        }
12208    }
12209    
12210    return {
12211        register : function(btn){
12212            if(!btn.toggleGroup){
12213                return;
12214            }
12215            var g = groups[btn.toggleGroup];
12216            if(!g){
12217                g = groups[btn.toggleGroup] = [];
12218            }
12219            g.push(btn);
12220            btn.on("toggle", toggleGroup);
12221        },
12222        
12223        unregister : function(btn){
12224            if(!btn.toggleGroup){
12225                return;
12226            }
12227            var g = groups[btn.toggleGroup];
12228            if(g){
12229                g.remove(btn);
12230                btn.un("toggle", toggleGroup);
12231            }
12232        }
12233    };
12234 }();/*
12235  * Based on:
12236  * Ext JS Library 1.1.1
12237  * Copyright(c) 2006-2007, Ext JS, LLC.
12238  *
12239  * Originally Released Under LGPL - original licence link has changed is not relivant.
12240  *
12241  * Fork - LGPL
12242  * <script type="text/javascript">
12243  */
12244  
12245 /**
12246  * @class Roo.SplitButton
12247  * @extends Roo.Button
12248  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
12249  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
12250  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
12251  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
12252  * @cfg {String} arrowTooltip The title attribute of the arrow
12253  * @constructor
12254  * Create a new menu button
12255  * @param {String/HTMLElement/Element} renderTo The element to append the button to
12256  * @param {Object} config The config object
12257  */
12258 Roo.SplitButton = function(renderTo, config){
12259     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
12260     /**
12261      * @event arrowclick
12262      * Fires when this button's arrow is clicked
12263      * @param {SplitButton} this
12264      * @param {EventObject} e The click event
12265      */
12266     this.addEvents({"arrowclick":true});
12267 };
12268
12269 Roo.extend(Roo.SplitButton, Roo.Button, {
12270     render : function(renderTo){
12271         // this is one sweet looking template!
12272         var tpl = new Roo.Template(
12273             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
12274             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
12275             '<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>',
12276             "</tbody></table></td><td>",
12277             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
12278             '<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>',
12279             "</tbody></table></td></tr></table>"
12280         );
12281         var btn = tpl.append(renderTo, [this.text, this.type], true);
12282         var btnEl = btn.child("button");
12283         if(this.cls){
12284             btn.addClass(this.cls);
12285         }
12286         if(this.icon){
12287             btnEl.setStyle('background-image', 'url(' +this.icon +')');
12288         }
12289         if(this.iconCls){
12290             btnEl.addClass(this.iconCls);
12291             if(!this.cls){
12292                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
12293             }
12294         }
12295         this.el = btn;
12296         if(this.handleMouseEvents){
12297             btn.on("mouseover", this.onMouseOver, this);
12298             btn.on("mouseout", this.onMouseOut, this);
12299             btn.on("mousedown", this.onMouseDown, this);
12300             btn.on("mouseup", this.onMouseUp, this);
12301         }
12302         btn.on(this.clickEvent, this.onClick, this);
12303         if(this.tooltip){
12304             if(typeof this.tooltip == 'object'){
12305                 Roo.QuickTips.tips(Roo.apply({
12306                       target: btnEl.id
12307                 }, this.tooltip));
12308             } else {
12309                 btnEl.dom[this.tooltipType] = this.tooltip;
12310             }
12311         }
12312         if(this.arrowTooltip){
12313             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
12314         }
12315         if(this.hidden){
12316             this.hide();
12317         }
12318         if(this.disabled){
12319             this.disable();
12320         }
12321         if(this.pressed){
12322             this.el.addClass("x-btn-pressed");
12323         }
12324         if(Roo.isIE && !Roo.isIE7){
12325             this.autoWidth.defer(1, this);
12326         }else{
12327             this.autoWidth();
12328         }
12329         if(this.menu){
12330             this.menu.on("show", this.onMenuShow, this);
12331             this.menu.on("hide", this.onMenuHide, this);
12332         }
12333         this.fireEvent('render', this);
12334     },
12335
12336     // private
12337     autoWidth : function(){
12338         if(this.el){
12339             var tbl = this.el.child("table:first");
12340             var tbl2 = this.el.child("table:last");
12341             this.el.setWidth("auto");
12342             tbl.setWidth("auto");
12343             if(Roo.isIE7 && Roo.isStrict){
12344                 var ib = this.el.child('button:first');
12345                 if(ib && ib.getWidth() > 20){
12346                     ib.clip();
12347                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
12348                 }
12349             }
12350             if(this.minWidth){
12351                 if(this.hidden){
12352                     this.el.beginMeasure();
12353                 }
12354                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
12355                     tbl.setWidth(this.minWidth-tbl2.getWidth());
12356                 }
12357                 if(this.hidden){
12358                     this.el.endMeasure();
12359                 }
12360             }
12361             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
12362         } 
12363     },
12364     /**
12365      * Sets this button's click handler
12366      * @param {Function} handler The function to call when the button is clicked
12367      * @param {Object} scope (optional) Scope for the function passed above
12368      */
12369     setHandler : function(handler, scope){
12370         this.handler = handler;
12371         this.scope = scope;  
12372     },
12373     
12374     /**
12375      * Sets this button's arrow click handler
12376      * @param {Function} handler The function to call when the arrow is clicked
12377      * @param {Object} scope (optional) Scope for the function passed above
12378      */
12379     setArrowHandler : function(handler, scope){
12380         this.arrowHandler = handler;
12381         this.scope = scope;  
12382     },
12383     
12384     /**
12385      * Focus the button
12386      */
12387     focus : function(){
12388         if(this.el){
12389             this.el.child("button:first").focus();
12390         }
12391     },
12392
12393     // private
12394     onClick : function(e){
12395         e.preventDefault();
12396         if(!this.disabled){
12397             if(e.getTarget(".x-btn-menu-arrow-wrap")){
12398                 if(this.menu && !this.menu.isVisible()){
12399                     this.menu.show(this.el, this.menuAlign);
12400                 }
12401                 this.fireEvent("arrowclick", this, e);
12402                 if(this.arrowHandler){
12403                     this.arrowHandler.call(this.scope || this, this, e);
12404                 }
12405             }else{
12406                 this.fireEvent("click", this, e);
12407                 if(this.handler){
12408                     this.handler.call(this.scope || this, this, e);
12409                 }
12410             }
12411         }
12412     },
12413     // private
12414     onMouseDown : function(e){
12415         if(!this.disabled){
12416             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
12417         }
12418     },
12419     // private
12420     onMouseUp : function(e){
12421         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
12422     }   
12423 });
12424
12425
12426 // backwards compat
12427 Roo.MenuButton = Roo.SplitButton;/*
12428  * Based on:
12429  * Ext JS Library 1.1.1
12430  * Copyright(c) 2006-2007, Ext JS, LLC.
12431  *
12432  * Originally Released Under LGPL - original licence link has changed is not relivant.
12433  *
12434  * Fork - LGPL
12435  * <script type="text/javascript">
12436  */
12437
12438 /**
12439  * @class Roo.Toolbar
12440  * Basic Toolbar class.
12441  * @constructor
12442  * Creates a new Toolbar
12443  * @param {Object} container The config object
12444  */ 
12445 Roo.Toolbar = function(container, buttons, config)
12446 {
12447     /// old consturctor format still supported..
12448     if(container instanceof Array){ // omit the container for later rendering
12449         buttons = container;
12450         config = buttons;
12451         container = null;
12452     }
12453     if (typeof(container) == 'object' && container.xtype) {
12454         config = container;
12455         container = config.container;
12456         buttons = config.buttons || []; // not really - use items!!
12457     }
12458     var xitems = [];
12459     if (config && config.items) {
12460         xitems = config.items;
12461         delete config.items;
12462     }
12463     Roo.apply(this, config);
12464     this.buttons = buttons;
12465     
12466     if(container){
12467         this.render(container);
12468     }
12469     this.xitems = xitems;
12470     Roo.each(xitems, function(b) {
12471         this.add(b);
12472     }, this);
12473     
12474 };
12475
12476 Roo.Toolbar.prototype = {
12477     /**
12478      * @cfg {Array} items
12479      * array of button configs or elements to add (will be converted to a MixedCollection)
12480      */
12481     
12482     /**
12483      * @cfg {String/HTMLElement/Element} container
12484      * The id or element that will contain the toolbar
12485      */
12486     // private
12487     render : function(ct){
12488         this.el = Roo.get(ct);
12489         if(this.cls){
12490             this.el.addClass(this.cls);
12491         }
12492         // using a table allows for vertical alignment
12493         // 100% width is needed by Safari...
12494         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
12495         this.tr = this.el.child("tr", true);
12496         var autoId = 0;
12497         this.items = new Roo.util.MixedCollection(false, function(o){
12498             return o.id || ("item" + (++autoId));
12499         });
12500         if(this.buttons){
12501             this.add.apply(this, this.buttons);
12502             delete this.buttons;
12503         }
12504     },
12505
12506     /**
12507      * Adds element(s) to the toolbar -- this function takes a variable number of 
12508      * arguments of mixed type and adds them to the toolbar.
12509      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
12510      * <ul>
12511      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
12512      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
12513      * <li>Field: Any form field (equivalent to {@link #addField})</li>
12514      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
12515      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
12516      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
12517      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
12518      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
12519      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
12520      * </ul>
12521      * @param {Mixed} arg2
12522      * @param {Mixed} etc.
12523      */
12524     add : function(){
12525         var a = arguments, l = a.length;
12526         for(var i = 0; i < l; i++){
12527             this._add(a[i]);
12528         }
12529     },
12530     // private..
12531     _add : function(el) {
12532         
12533         if (el.xtype) {
12534             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
12535         }
12536         
12537         if (el.applyTo){ // some kind of form field
12538             return this.addField(el);
12539         } 
12540         if (el.render){ // some kind of Toolbar.Item
12541             return this.addItem(el);
12542         }
12543         if (typeof el == "string"){ // string
12544             if(el == "separator" || el == "-"){
12545                 return this.addSeparator();
12546             }
12547             if (el == " "){
12548                 return this.addSpacer();
12549             }
12550             if(el == "->"){
12551                 return this.addFill();
12552             }
12553             return this.addText(el);
12554             
12555         }
12556         if(el.tagName){ // element
12557             return this.addElement(el);
12558         }
12559         if(typeof el == "object"){ // must be button config?
12560             return this.addButton(el);
12561         }
12562         // and now what?!?!
12563         return false;
12564         
12565     },
12566     
12567     /**
12568      * Add an Xtype element
12569      * @param {Object} xtype Xtype Object
12570      * @return {Object} created Object
12571      */
12572     addxtype : function(e){
12573         return this.add(e);  
12574     },
12575     
12576     /**
12577      * Returns the Element for this toolbar.
12578      * @return {Roo.Element}
12579      */
12580     getEl : function(){
12581         return this.el;  
12582     },
12583     
12584     /**
12585      * Adds a separator
12586      * @return {Roo.Toolbar.Item} The separator item
12587      */
12588     addSeparator : function(){
12589         return this.addItem(new Roo.Toolbar.Separator());
12590     },
12591
12592     /**
12593      * Adds a spacer element
12594      * @return {Roo.Toolbar.Spacer} The spacer item
12595      */
12596     addSpacer : function(){
12597         return this.addItem(new Roo.Toolbar.Spacer());
12598     },
12599
12600     /**
12601      * Adds a fill element that forces subsequent additions to the right side of the toolbar
12602      * @return {Roo.Toolbar.Fill} The fill item
12603      */
12604     addFill : function(){
12605         return this.addItem(new Roo.Toolbar.Fill());
12606     },
12607
12608     /**
12609      * Adds any standard HTML element to the toolbar
12610      * @param {String/HTMLElement/Element} el The element or id of the element to add
12611      * @return {Roo.Toolbar.Item} The element's item
12612      */
12613     addElement : function(el){
12614         return this.addItem(new Roo.Toolbar.Item(el));
12615     },
12616     /**
12617      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
12618      * @type Roo.util.MixedCollection  
12619      */
12620     items : false,
12621      
12622     /**
12623      * Adds any Toolbar.Item or subclass
12624      * @param {Roo.Toolbar.Item} item
12625      * @return {Roo.Toolbar.Item} The item
12626      */
12627     addItem : function(item){
12628         var td = this.nextBlock();
12629         item.render(td);
12630         this.items.add(item);
12631         return item;
12632     },
12633     
12634     /**
12635      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
12636      * @param {Object/Array} config A button config or array of configs
12637      * @return {Roo.Toolbar.Button/Array}
12638      */
12639     addButton : function(config){
12640         if(config instanceof Array){
12641             var buttons = [];
12642             for(var i = 0, len = config.length; i < len; i++) {
12643                 buttons.push(this.addButton(config[i]));
12644             }
12645             return buttons;
12646         }
12647         var b = config;
12648         if(!(config instanceof Roo.Toolbar.Button)){
12649             b = config.split ?
12650                 new Roo.Toolbar.SplitButton(config) :
12651                 new Roo.Toolbar.Button(config);
12652         }
12653         var td = this.nextBlock();
12654         b.render(td);
12655         this.items.add(b);
12656         return b;
12657     },
12658     
12659     /**
12660      * Adds text to the toolbar
12661      * @param {String} text The text to add
12662      * @return {Roo.Toolbar.Item} The element's item
12663      */
12664     addText : function(text){
12665         return this.addItem(new Roo.Toolbar.TextItem(text));
12666     },
12667     
12668     /**
12669      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
12670      * @param {Number} index The index where the item is to be inserted
12671      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
12672      * @return {Roo.Toolbar.Button/Item}
12673      */
12674     insertButton : function(index, item){
12675         if(item instanceof Array){
12676             var buttons = [];
12677             for(var i = 0, len = item.length; i < len; i++) {
12678                buttons.push(this.insertButton(index + i, item[i]));
12679             }
12680             return buttons;
12681         }
12682         if (!(item instanceof Roo.Toolbar.Button)){
12683            item = new Roo.Toolbar.Button(item);
12684         }
12685         var td = document.createElement("td");
12686         this.tr.insertBefore(td, this.tr.childNodes[index]);
12687         item.render(td);
12688         this.items.insert(index, item);
12689         return item;
12690     },
12691     
12692     /**
12693      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12694      * @param {Object} config
12695      * @return {Roo.Toolbar.Item} The element's item
12696      */
12697     addDom : function(config, returnEl){
12698         var td = this.nextBlock();
12699         Roo.DomHelper.overwrite(td, config);
12700         var ti = new Roo.Toolbar.Item(td.firstChild);
12701         ti.render(td);
12702         this.items.add(ti);
12703         return ti;
12704     },
12705
12706     /**
12707      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12708      * @type Roo.util.MixedCollection  
12709      */
12710     fields : false,
12711     
12712     /**
12713      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
12714      * Note: the field should not have been rendered yet. For a field that has already been
12715      * rendered, use {@link #addElement}.
12716      * @param {Roo.form.Field} field
12717      * @return {Roo.ToolbarItem}
12718      */
12719      
12720       
12721     addField : function(field) {
12722         if (!this.fields) {
12723             var autoId = 0;
12724             this.fields = new Roo.util.MixedCollection(false, function(o){
12725                 return o.id || ("item" + (++autoId));
12726             });
12727
12728         }
12729         
12730         var td = this.nextBlock();
12731         field.render(td);
12732         var ti = new Roo.Toolbar.Item(td.firstChild);
12733         ti.render(td);
12734         this.items.add(ti);
12735         this.fields.add(field);
12736         return ti;
12737     },
12738     /**
12739      * Hide the toolbar
12740      * @method hide
12741      */
12742      
12743       
12744     hide : function()
12745     {
12746         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12747         this.el.child('div').hide();
12748     },
12749     /**
12750      * Show the toolbar
12751      * @method show
12752      */
12753     show : function()
12754     {
12755         this.el.child('div').show();
12756     },
12757       
12758     // private
12759     nextBlock : function(){
12760         var td = document.createElement("td");
12761         this.tr.appendChild(td);
12762         return td;
12763     },
12764
12765     // private
12766     destroy : function(){
12767         if(this.items){ // rendered?
12768             Roo.destroy.apply(Roo, this.items.items);
12769         }
12770         if(this.fields){ // rendered?
12771             Roo.destroy.apply(Roo, this.fields.items);
12772         }
12773         Roo.Element.uncache(this.el, this.tr);
12774     }
12775 };
12776
12777 /**
12778  * @class Roo.Toolbar.Item
12779  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12780  * @constructor
12781  * Creates a new Item
12782  * @param {HTMLElement} el 
12783  */
12784 Roo.Toolbar.Item = function(el){
12785     this.el = Roo.getDom(el);
12786     this.id = Roo.id(this.el);
12787     this.hidden = false;
12788 };
12789
12790 Roo.Toolbar.Item.prototype = {
12791     
12792     /**
12793      * Get this item's HTML Element
12794      * @return {HTMLElement}
12795      */
12796     getEl : function(){
12797        return this.el;  
12798     },
12799
12800     // private
12801     render : function(td){
12802         this.td = td;
12803         td.appendChild(this.el);
12804     },
12805     
12806     /**
12807      * Removes and destroys this item.
12808      */
12809     destroy : function(){
12810         this.td.parentNode.removeChild(this.td);
12811     },
12812     
12813     /**
12814      * Shows this item.
12815      */
12816     show: function(){
12817         this.hidden = false;
12818         this.td.style.display = "";
12819     },
12820     
12821     /**
12822      * Hides this item.
12823      */
12824     hide: function(){
12825         this.hidden = true;
12826         this.td.style.display = "none";
12827     },
12828     
12829     /**
12830      * Convenience function for boolean show/hide.
12831      * @param {Boolean} visible true to show/false to hide
12832      */
12833     setVisible: function(visible){
12834         if(visible) {
12835             this.show();
12836         }else{
12837             this.hide();
12838         }
12839     },
12840     
12841     /**
12842      * Try to focus this item.
12843      */
12844     focus : function(){
12845         Roo.fly(this.el).focus();
12846     },
12847     
12848     /**
12849      * Disables this item.
12850      */
12851     disable : function(){
12852         Roo.fly(this.td).addClass("x-item-disabled");
12853         this.disabled = true;
12854         this.el.disabled = true;
12855     },
12856     
12857     /**
12858      * Enables this item.
12859      */
12860     enable : function(){
12861         Roo.fly(this.td).removeClass("x-item-disabled");
12862         this.disabled = false;
12863         this.el.disabled = false;
12864     }
12865 };
12866
12867
12868 /**
12869  * @class Roo.Toolbar.Separator
12870  * @extends Roo.Toolbar.Item
12871  * A simple toolbar separator class
12872  * @constructor
12873  * Creates a new Separator
12874  */
12875 Roo.Toolbar.Separator = function(){
12876     var s = document.createElement("span");
12877     s.className = "ytb-sep";
12878     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
12879 };
12880 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12881     enable:Roo.emptyFn,
12882     disable:Roo.emptyFn,
12883     focus:Roo.emptyFn
12884 });
12885
12886 /**
12887  * @class Roo.Toolbar.Spacer
12888  * @extends Roo.Toolbar.Item
12889  * A simple element that adds extra horizontal space to a toolbar.
12890  * @constructor
12891  * Creates a new Spacer
12892  */
12893 Roo.Toolbar.Spacer = function(){
12894     var s = document.createElement("div");
12895     s.className = "ytb-spacer";
12896     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
12897 };
12898 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12899     enable:Roo.emptyFn,
12900     disable:Roo.emptyFn,
12901     focus:Roo.emptyFn
12902 });
12903
12904 /**
12905  * @class Roo.Toolbar.Fill
12906  * @extends Roo.Toolbar.Spacer
12907  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12908  * @constructor
12909  * Creates a new Spacer
12910  */
12911 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12912     // private
12913     render : function(td){
12914         td.style.width = '100%';
12915         Roo.Toolbar.Fill.superclass.render.call(this, td);
12916     }
12917 });
12918
12919 /**
12920  * @class Roo.Toolbar.TextItem
12921  * @extends Roo.Toolbar.Item
12922  * A simple class that renders text directly into a toolbar.
12923  * @constructor
12924  * Creates a new TextItem
12925  * @param {String} text
12926  */
12927 Roo.Toolbar.TextItem = function(text){
12928     if (typeof(text) == 'object') {
12929         text = text.text;
12930     }
12931     var s = document.createElement("span");
12932     s.className = "ytb-text";
12933     s.innerHTML = text;
12934     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
12935 };
12936 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12937     enable:Roo.emptyFn,
12938     disable:Roo.emptyFn,
12939     focus:Roo.emptyFn
12940 });
12941
12942 /**
12943  * @class Roo.Toolbar.Button
12944  * @extends Roo.Button
12945  * A button that renders into a toolbar.
12946  * @constructor
12947  * Creates a new Button
12948  * @param {Object} config A standard {@link Roo.Button} config object
12949  */
12950 Roo.Toolbar.Button = function(config){
12951     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12952 };
12953 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12954     render : function(td){
12955         this.td = td;
12956         Roo.Toolbar.Button.superclass.render.call(this, td);
12957     },
12958     
12959     /**
12960      * Removes and destroys this button
12961      */
12962     destroy : function(){
12963         Roo.Toolbar.Button.superclass.destroy.call(this);
12964         this.td.parentNode.removeChild(this.td);
12965     },
12966     
12967     /**
12968      * Shows this button
12969      */
12970     show: function(){
12971         this.hidden = false;
12972         this.td.style.display = "";
12973     },
12974     
12975     /**
12976      * Hides this button
12977      */
12978     hide: function(){
12979         this.hidden = true;
12980         this.td.style.display = "none";
12981     },
12982
12983     /**
12984      * Disables this item
12985      */
12986     disable : function(){
12987         Roo.fly(this.td).addClass("x-item-disabled");
12988         this.disabled = true;
12989     },
12990
12991     /**
12992      * Enables this item
12993      */
12994     enable : function(){
12995         Roo.fly(this.td).removeClass("x-item-disabled");
12996         this.disabled = false;
12997     }
12998 });
12999 // backwards compat
13000 Roo.ToolbarButton = Roo.Toolbar.Button;
13001
13002 /**
13003  * @class Roo.Toolbar.SplitButton
13004  * @extends Roo.SplitButton
13005  * A menu button that renders into a toolbar.
13006  * @constructor
13007  * Creates a new SplitButton
13008  * @param {Object} config A standard {@link Roo.SplitButton} config object
13009  */
13010 Roo.Toolbar.SplitButton = function(config){
13011     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
13012 };
13013 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
13014     render : function(td){
13015         this.td = td;
13016         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
13017     },
13018     
13019     /**
13020      * Removes and destroys this button
13021      */
13022     destroy : function(){
13023         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
13024         this.td.parentNode.removeChild(this.td);
13025     },
13026     
13027     /**
13028      * Shows this button
13029      */
13030     show: function(){
13031         this.hidden = false;
13032         this.td.style.display = "";
13033     },
13034     
13035     /**
13036      * Hides this button
13037      */
13038     hide: function(){
13039         this.hidden = true;
13040         this.td.style.display = "none";
13041     }
13042 });
13043
13044 // backwards compat
13045 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
13046  * Based on:
13047  * Ext JS Library 1.1.1
13048  * Copyright(c) 2006-2007, Ext JS, LLC.
13049  *
13050  * Originally Released Under LGPL - original licence link has changed is not relivant.
13051  *
13052  * Fork - LGPL
13053  * <script type="text/javascript">
13054  */
13055  
13056 /**
13057  * @class Roo.PagingToolbar
13058  * @extends Roo.Toolbar
13059  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
13060  * @constructor
13061  * Create a new PagingToolbar
13062  * @param {Object} config The config object
13063  */
13064 Roo.PagingToolbar = function(el, ds, config)
13065 {
13066     // old args format still supported... - xtype is prefered..
13067     if (typeof(el) == 'object' && el.xtype) {
13068         // created from xtype...
13069         config = el;
13070         ds = el.dataSource;
13071         el = config.container;
13072     }
13073     var items = [];
13074     if (config.items) {
13075         items = config.items;
13076         config.items = [];
13077     }
13078     
13079     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
13080     this.ds = ds;
13081     this.cursor = 0;
13082     this.renderButtons(this.el);
13083     this.bind(ds);
13084     
13085     // supprot items array.
13086    
13087     Roo.each(items, function(e) {
13088         this.add(Roo.factory(e));
13089     },this);
13090     
13091 };
13092
13093 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
13094     /**
13095      * @cfg {Roo.data.Store} dataSource
13096      * The underlying data store providing the paged data
13097      */
13098     /**
13099      * @cfg {String/HTMLElement/Element} container
13100      * container The id or element that will contain the toolbar
13101      */
13102     /**
13103      * @cfg {Boolean} displayInfo
13104      * True to display the displayMsg (defaults to false)
13105      */
13106     /**
13107      * @cfg {Number} pageSize
13108      * The number of records to display per page (defaults to 20)
13109      */
13110     pageSize: 20,
13111     /**
13112      * @cfg {String} displayMsg
13113      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
13114      */
13115     displayMsg : 'Displaying {0} - {1} of {2}',
13116     /**
13117      * @cfg {String} emptyMsg
13118      * The message to display when no records are found (defaults to "No data to display")
13119      */
13120     emptyMsg : 'No data to display',
13121     /**
13122      * Customizable piece of the default paging text (defaults to "Page")
13123      * @type String
13124      */
13125     beforePageText : "Page",
13126     /**
13127      * Customizable piece of the default paging text (defaults to "of %0")
13128      * @type String
13129      */
13130     afterPageText : "of {0}",
13131     /**
13132      * Customizable piece of the default paging text (defaults to "First Page")
13133      * @type String
13134      */
13135     firstText : "First Page",
13136     /**
13137      * Customizable piece of the default paging text (defaults to "Previous Page")
13138      * @type String
13139      */
13140     prevText : "Previous Page",
13141     /**
13142      * Customizable piece of the default paging text (defaults to "Next Page")
13143      * @type String
13144      */
13145     nextText : "Next Page",
13146     /**
13147      * Customizable piece of the default paging text (defaults to "Last Page")
13148      * @type String
13149      */
13150     lastText : "Last Page",
13151     /**
13152      * Customizable piece of the default paging text (defaults to "Refresh")
13153      * @type String
13154      */
13155     refreshText : "Refresh",
13156
13157     // private
13158     renderButtons : function(el){
13159         Roo.PagingToolbar.superclass.render.call(this, el);
13160         this.first = this.addButton({
13161             tooltip: this.firstText,
13162             cls: "x-btn-icon x-grid-page-first",
13163             disabled: true,
13164             handler: this.onClick.createDelegate(this, ["first"])
13165         });
13166         this.prev = this.addButton({
13167             tooltip: this.prevText,
13168             cls: "x-btn-icon x-grid-page-prev",
13169             disabled: true,
13170             handler: this.onClick.createDelegate(this, ["prev"])
13171         });
13172         //this.addSeparator();
13173         this.add(this.beforePageText);
13174         this.field = Roo.get(this.addDom({
13175            tag: "input",
13176            type: "text",
13177            size: "3",
13178            value: "1",
13179            cls: "x-grid-page-number"
13180         }).el);
13181         this.field.on("keydown", this.onPagingKeydown, this);
13182         this.field.on("focus", function(){this.dom.select();});
13183         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
13184         this.field.setHeight(18);
13185         //this.addSeparator();
13186         this.next = this.addButton({
13187             tooltip: this.nextText,
13188             cls: "x-btn-icon x-grid-page-next",
13189             disabled: true,
13190             handler: this.onClick.createDelegate(this, ["next"])
13191         });
13192         this.last = this.addButton({
13193             tooltip: this.lastText,
13194             cls: "x-btn-icon x-grid-page-last",
13195             disabled: true,
13196             handler: this.onClick.createDelegate(this, ["last"])
13197         });
13198         //this.addSeparator();
13199         this.loading = this.addButton({
13200             tooltip: this.refreshText,
13201             cls: "x-btn-icon x-grid-loading",
13202             handler: this.onClick.createDelegate(this, ["refresh"])
13203         });
13204
13205         if(this.displayInfo){
13206             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
13207         }
13208     },
13209
13210     // private
13211     updateInfo : function(){
13212         if(this.displayEl){
13213             var count = this.ds.getCount();
13214             var msg = count == 0 ?
13215                 this.emptyMsg :
13216                 String.format(
13217                     this.displayMsg,
13218                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
13219                 );
13220             this.displayEl.update(msg);
13221         }
13222     },
13223
13224     // private
13225     onLoad : function(ds, r, o){
13226        this.cursor = o.params ? o.params.start : 0;
13227        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
13228
13229        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
13230        this.field.dom.value = ap;
13231        this.first.setDisabled(ap == 1);
13232        this.prev.setDisabled(ap == 1);
13233        this.next.setDisabled(ap == ps);
13234        this.last.setDisabled(ap == ps);
13235        this.loading.enable();
13236        this.updateInfo();
13237     },
13238
13239     // private
13240     getPageData : function(){
13241         var total = this.ds.getTotalCount();
13242         return {
13243             total : total,
13244             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
13245             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
13246         };
13247     },
13248
13249     // private
13250     onLoadError : function(){
13251         this.loading.enable();
13252     },
13253
13254     // private
13255     onPagingKeydown : function(e){
13256         var k = e.getKey();
13257         var d = this.getPageData();
13258         if(k == e.RETURN){
13259             var v = this.field.dom.value, pageNum;
13260             if(!v || isNaN(pageNum = parseInt(v, 10))){
13261                 this.field.dom.value = d.activePage;
13262                 return;
13263             }
13264             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
13265             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13266             e.stopEvent();
13267         }
13268         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))
13269         {
13270           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
13271           this.field.dom.value = pageNum;
13272           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
13273           e.stopEvent();
13274         }
13275         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13276         {
13277           var v = this.field.dom.value, pageNum; 
13278           var increment = (e.shiftKey) ? 10 : 1;
13279           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13280             increment *= -1;
13281           if(!v || isNaN(pageNum = parseInt(v, 10))) {
13282             this.field.dom.value = d.activePage;
13283             return;
13284           }
13285           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
13286           {
13287             this.field.dom.value = parseInt(v, 10) + increment;
13288             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
13289             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13290           }
13291           e.stopEvent();
13292         }
13293     },
13294
13295     // private
13296     beforeLoad : function(){
13297         if(this.loading){
13298             this.loading.disable();
13299         }
13300     },
13301
13302     // private
13303     onClick : function(which){
13304         var ds = this.ds;
13305         switch(which){
13306             case "first":
13307                 ds.load({params:{start: 0, limit: this.pageSize}});
13308             break;
13309             case "prev":
13310                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
13311             break;
13312             case "next":
13313                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
13314             break;
13315             case "last":
13316                 var total = ds.getTotalCount();
13317                 var extra = total % this.pageSize;
13318                 var lastStart = extra ? (total - extra) : total-this.pageSize;
13319                 ds.load({params:{start: lastStart, limit: this.pageSize}});
13320             break;
13321             case "refresh":
13322                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
13323             break;
13324         }
13325     },
13326
13327     /**
13328      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
13329      * @param {Roo.data.Store} store The data store to unbind
13330      */
13331     unbind : function(ds){
13332         ds.un("beforeload", this.beforeLoad, this);
13333         ds.un("load", this.onLoad, this);
13334         ds.un("loadexception", this.onLoadError, this);
13335         ds.un("remove", this.updateInfo, this);
13336         ds.un("add", this.updateInfo, this);
13337         this.ds = undefined;
13338     },
13339
13340     /**
13341      * Binds the paging toolbar to the specified {@link Roo.data.Store}
13342      * @param {Roo.data.Store} store The data store to bind
13343      */
13344     bind : function(ds){
13345         ds.on("beforeload", this.beforeLoad, this);
13346         ds.on("load", this.onLoad, this);
13347         ds.on("loadexception", this.onLoadError, this);
13348         ds.on("remove", this.updateInfo, this);
13349         ds.on("add", this.updateInfo, this);
13350         this.ds = ds;
13351     }
13352 });/*
13353  * Based on:
13354  * Ext JS Library 1.1.1
13355  * Copyright(c) 2006-2007, Ext JS, LLC.
13356  *
13357  * Originally Released Under LGPL - original licence link has changed is not relivant.
13358  *
13359  * Fork - LGPL
13360  * <script type="text/javascript">
13361  */
13362
13363 /**
13364  * @class Roo.Resizable
13365  * @extends Roo.util.Observable
13366  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
13367  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
13368  * 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
13369  * the element will be wrapped for you automatically.</p>
13370  * <p>Here is the list of valid resize handles:</p>
13371  * <pre>
13372 Value   Description
13373 ------  -------------------
13374  'n'     north
13375  's'     south
13376  'e'     east
13377  'w'     west
13378  'nw'    northwest
13379  'sw'    southwest
13380  'se'    southeast
13381  'ne'    northeast
13382  'hd'    horizontal drag
13383  'all'   all
13384 </pre>
13385  * <p>Here's an example showing the creation of a typical Resizable:</p>
13386  * <pre><code>
13387 var resizer = new Roo.Resizable("element-id", {
13388     handles: 'all',
13389     minWidth: 200,
13390     minHeight: 100,
13391     maxWidth: 500,
13392     maxHeight: 400,
13393     pinned: true
13394 });
13395 resizer.on("resize", myHandler);
13396 </code></pre>
13397  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
13398  * resizer.east.setDisplayed(false);</p>
13399  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
13400  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
13401  * resize operation's new size (defaults to [0, 0])
13402  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
13403  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
13404  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
13405  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
13406  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
13407  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
13408  * @cfg {Number} width The width of the element in pixels (defaults to null)
13409  * @cfg {Number} height The height of the element in pixels (defaults to null)
13410  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
13411  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
13412  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
13413  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
13414  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
13415  * in favor of the handles config option (defaults to false)
13416  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
13417  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
13418  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
13419  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
13420  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
13421  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
13422  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
13423  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
13424  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
13425  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
13426  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
13427  * @constructor
13428  * Create a new resizable component
13429  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
13430  * @param {Object} config configuration options
13431   */
13432 Roo.Resizable = function(el, config)
13433 {
13434     this.el = Roo.get(el);
13435
13436     if(config && config.wrap){
13437         config.resizeChild = this.el;
13438         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
13439         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
13440         this.el.setStyle("overflow", "hidden");
13441         this.el.setPositioning(config.resizeChild.getPositioning());
13442         config.resizeChild.clearPositioning();
13443         if(!config.width || !config.height){
13444             var csize = config.resizeChild.getSize();
13445             this.el.setSize(csize.width, csize.height);
13446         }
13447         if(config.pinned && !config.adjustments){
13448             config.adjustments = "auto";
13449         }
13450     }
13451
13452     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
13453     this.proxy.unselectable();
13454     this.proxy.enableDisplayMode('block');
13455
13456     Roo.apply(this, config);
13457
13458     if(this.pinned){
13459         this.disableTrackOver = true;
13460         this.el.addClass("x-resizable-pinned");
13461     }
13462     // if the element isn't positioned, make it relative
13463     var position = this.el.getStyle("position");
13464     if(position != "absolute" && position != "fixed"){
13465         this.el.setStyle("position", "relative");
13466     }
13467     if(!this.handles){ // no handles passed, must be legacy style
13468         this.handles = 's,e,se';
13469         if(this.multiDirectional){
13470             this.handles += ',n,w';
13471         }
13472     }
13473     if(this.handles == "all"){
13474         this.handles = "n s e w ne nw se sw";
13475     }
13476     var hs = this.handles.split(/\s*?[,;]\s*?| /);
13477     var ps = Roo.Resizable.positions;
13478     for(var i = 0, len = hs.length; i < len; i++){
13479         if(hs[i] && ps[hs[i]]){
13480             var pos = ps[hs[i]];
13481             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
13482         }
13483     }
13484     // legacy
13485     this.corner = this.southeast;
13486     
13487     // updateBox = the box can move..
13488     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
13489         this.updateBox = true;
13490     }
13491
13492     this.activeHandle = null;
13493
13494     if(this.resizeChild){
13495         if(typeof this.resizeChild == "boolean"){
13496             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
13497         }else{
13498             this.resizeChild = Roo.get(this.resizeChild, true);
13499         }
13500     }
13501     
13502     if(this.adjustments == "auto"){
13503         var rc = this.resizeChild;
13504         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
13505         if(rc && (hw || hn)){
13506             rc.position("relative");
13507             rc.setLeft(hw ? hw.el.getWidth() : 0);
13508             rc.setTop(hn ? hn.el.getHeight() : 0);
13509         }
13510         this.adjustments = [
13511             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
13512             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
13513         ];
13514     }
13515
13516     if(this.draggable){
13517         this.dd = this.dynamic ?
13518             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
13519         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
13520     }
13521
13522     // public events
13523     this.addEvents({
13524         /**
13525          * @event beforeresize
13526          * Fired before resize is allowed. Set enabled to false to cancel resize.
13527          * @param {Roo.Resizable} this
13528          * @param {Roo.EventObject} e The mousedown event
13529          */
13530         "beforeresize" : true,
13531         "resizing" : true,
13532         /**
13533          * @event resize
13534          * Fired after a resize.
13535          * @param {Roo.Resizable} this
13536          * @param {Number} width The new width
13537          * @param {Number} height The new height
13538          * @param {Roo.EventObject} e The mouseup event
13539          */
13540         "resize" : true
13541     });
13542
13543     if(this.width !== null && this.height !== null){
13544         this.resizeTo(this.width, this.height);
13545     }else{
13546         this.updateChildSize();
13547     }
13548     if(Roo.isIE){
13549         this.el.dom.style.zoom = 1;
13550     }
13551     Roo.Resizable.superclass.constructor.call(this);
13552 };
13553
13554 Roo.extend(Roo.Resizable, Roo.util.Observable, {
13555         resizeChild : false,
13556         adjustments : [0, 0],
13557         minWidth : 5,
13558         minHeight : 5,
13559         maxWidth : 10000,
13560         maxHeight : 10000,
13561         enabled : true,
13562         animate : false,
13563         duration : .35,
13564         dynamic : false,
13565         handles : false,
13566         multiDirectional : false,
13567         disableTrackOver : false,
13568         easing : 'easeOutStrong',
13569         widthIncrement : 0,
13570         heightIncrement : 0,
13571         pinned : false,
13572         width : null,
13573         height : null,
13574         preserveRatio : false,
13575         transparent: false,
13576         minX: 0,
13577         minY: 0,
13578         draggable: false,
13579
13580         /**
13581          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
13582          */
13583         constrainTo: undefined,
13584         /**
13585          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
13586          */
13587         resizeRegion: undefined,
13588
13589
13590     /**
13591      * Perform a manual resize
13592      * @param {Number} width
13593      * @param {Number} height
13594      */
13595     resizeTo : function(width, height){
13596         this.el.setSize(width, height);
13597         this.updateChildSize();
13598         this.fireEvent("resize", this, width, height, null);
13599     },
13600
13601     // private
13602     startSizing : function(e, handle){
13603         this.fireEvent("beforeresize", this, e);
13604         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
13605
13606             if(!this.overlay){
13607                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
13608                 this.overlay.unselectable();
13609                 this.overlay.enableDisplayMode("block");
13610                 this.overlay.on("mousemove", this.onMouseMove, this);
13611                 this.overlay.on("mouseup", this.onMouseUp, this);
13612             }
13613             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
13614
13615             this.resizing = true;
13616             this.startBox = this.el.getBox();
13617             this.startPoint = e.getXY();
13618             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
13619                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
13620
13621             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13622             this.overlay.show();
13623
13624             if(this.constrainTo) {
13625                 var ct = Roo.get(this.constrainTo);
13626                 this.resizeRegion = ct.getRegion().adjust(
13627                     ct.getFrameWidth('t'),
13628                     ct.getFrameWidth('l'),
13629                     -ct.getFrameWidth('b'),
13630                     -ct.getFrameWidth('r')
13631                 );
13632             }
13633
13634             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
13635             this.proxy.show();
13636             this.proxy.setBox(this.startBox);
13637             if(!this.dynamic){
13638                 this.proxy.setStyle('visibility', 'visible');
13639             }
13640         }
13641     },
13642
13643     // private
13644     onMouseDown : function(handle, e){
13645         if(this.enabled){
13646             e.stopEvent();
13647             this.activeHandle = handle;
13648             this.startSizing(e, handle);
13649         }
13650     },
13651
13652     // private
13653     onMouseUp : function(e){
13654         var size = this.resizeElement();
13655         this.resizing = false;
13656         this.handleOut();
13657         this.overlay.hide();
13658         this.proxy.hide();
13659         this.fireEvent("resize", this, size.width, size.height, e);
13660     },
13661
13662     // private
13663     updateChildSize : function(){
13664         
13665         if(this.resizeChild){
13666             var el = this.el;
13667             var child = this.resizeChild;
13668             var adj = this.adjustments;
13669             if(el.dom.offsetWidth){
13670                 var b = el.getSize(true);
13671                 child.setSize(b.width+adj[0], b.height+adj[1]);
13672             }
13673             // Second call here for IE
13674             // The first call enables instant resizing and
13675             // the second call corrects scroll bars if they
13676             // exist
13677             if(Roo.isIE){
13678                 setTimeout(function(){
13679                     if(el.dom.offsetWidth){
13680                         var b = el.getSize(true);
13681                         child.setSize(b.width+adj[0], b.height+adj[1]);
13682                     }
13683                 }, 10);
13684             }
13685         }
13686     },
13687
13688     // private
13689     snap : function(value, inc, min){
13690         if(!inc || !value) return value;
13691         var newValue = value;
13692         var m = value % inc;
13693         if(m > 0){
13694             if(m > (inc/2)){
13695                 newValue = value + (inc-m);
13696             }else{
13697                 newValue = value - m;
13698             }
13699         }
13700         return Math.max(min, newValue);
13701     },
13702
13703     // private
13704     resizeElement : function(){
13705         var box = this.proxy.getBox();
13706         if(this.updateBox){
13707             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13708         }else{
13709             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13710         }
13711         this.updateChildSize();
13712         if(!this.dynamic){
13713             this.proxy.hide();
13714         }
13715         return box;
13716     },
13717
13718     // private
13719     constrain : function(v, diff, m, mx){
13720         if(v - diff < m){
13721             diff = v - m;
13722         }else if(v - diff > mx){
13723             diff = mx - v;
13724         }
13725         return diff;
13726     },
13727
13728     // private
13729     onMouseMove : function(e){
13730         
13731         if(this.enabled){
13732             try{// try catch so if something goes wrong the user doesn't get hung
13733
13734             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13735                 return;
13736             }
13737
13738             //var curXY = this.startPoint;
13739             var curSize = this.curSize || this.startBox;
13740             var x = this.startBox.x, y = this.startBox.y;
13741             var ox = x, oy = y;
13742             var w = curSize.width, h = curSize.height;
13743             var ow = w, oh = h;
13744             var mw = this.minWidth, mh = this.minHeight;
13745             var mxw = this.maxWidth, mxh = this.maxHeight;
13746             var wi = this.widthIncrement;
13747             var hi = this.heightIncrement;
13748
13749             var eventXY = e.getXY();
13750             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13751             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13752
13753             var pos = this.activeHandle.position;
13754
13755             switch(pos){
13756                 case "east":
13757                     w += diffX;
13758                     w = Math.min(Math.max(mw, w), mxw);
13759                     break;
13760              
13761                 case "south":
13762                     h += diffY;
13763                     h = Math.min(Math.max(mh, h), mxh);
13764                     break;
13765                 case "southeast":
13766                     w += diffX;
13767                     h += diffY;
13768                     w = Math.min(Math.max(mw, w), mxw);
13769                     h = Math.min(Math.max(mh, h), mxh);
13770                     break;
13771                 case "north":
13772                     diffY = this.constrain(h, diffY, mh, mxh);
13773                     y += diffY;
13774                     h -= diffY;
13775                     break;
13776                 case "hdrag":
13777                     
13778                     if (wi) {
13779                         var adiffX = Math.abs(diffX);
13780                         var sub = (adiffX % wi); // how much 
13781                         if (sub > (wi/2)) { // far enough to snap
13782                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
13783                         } else {
13784                             // remove difference.. 
13785                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
13786                         }
13787                     }
13788                     x += diffX;
13789                     x = Math.max(this.minX, x);
13790                     break;
13791                 case "west":
13792                     diffX = this.constrain(w, diffX, mw, mxw);
13793                     x += diffX;
13794                     w -= diffX;
13795                     break;
13796                 case "northeast":
13797                     w += diffX;
13798                     w = Math.min(Math.max(mw, w), mxw);
13799                     diffY = this.constrain(h, diffY, mh, mxh);
13800                     y += diffY;
13801                     h -= diffY;
13802                     break;
13803                 case "northwest":
13804                     diffX = this.constrain(w, diffX, mw, mxw);
13805                     diffY = this.constrain(h, diffY, mh, mxh);
13806                     y += diffY;
13807                     h -= diffY;
13808                     x += diffX;
13809                     w -= diffX;
13810                     break;
13811                case "southwest":
13812                     diffX = this.constrain(w, diffX, mw, mxw);
13813                     h += diffY;
13814                     h = Math.min(Math.max(mh, h), mxh);
13815                     x += diffX;
13816                     w -= diffX;
13817                     break;
13818             }
13819
13820             var sw = this.snap(w, wi, mw);
13821             var sh = this.snap(h, hi, mh);
13822             if(sw != w || sh != h){
13823                 switch(pos){
13824                     case "northeast":
13825                         y -= sh - h;
13826                     break;
13827                     case "north":
13828                         y -= sh - h;
13829                         break;
13830                     case "southwest":
13831                         x -= sw - w;
13832                     break;
13833                     case "west":
13834                         x -= sw - w;
13835                         break;
13836                     case "northwest":
13837                         x -= sw - w;
13838                         y -= sh - h;
13839                     break;
13840                 }
13841                 w = sw;
13842                 h = sh;
13843             }
13844
13845             if(this.preserveRatio){
13846                 switch(pos){
13847                     case "southeast":
13848                     case "east":
13849                         h = oh * (w/ow);
13850                         h = Math.min(Math.max(mh, h), mxh);
13851                         w = ow * (h/oh);
13852                        break;
13853                     case "south":
13854                         w = ow * (h/oh);
13855                         w = Math.min(Math.max(mw, w), mxw);
13856                         h = oh * (w/ow);
13857                         break;
13858                     case "northeast":
13859                         w = ow * (h/oh);
13860                         w = Math.min(Math.max(mw, w), mxw);
13861                         h = oh * (w/ow);
13862                     break;
13863                     case "north":
13864                         var tw = w;
13865                         w = ow * (h/oh);
13866                         w = Math.min(Math.max(mw, w), mxw);
13867                         h = oh * (w/ow);
13868                         x += (tw - w) / 2;
13869                         break;
13870                     case "southwest":
13871                         h = oh * (w/ow);
13872                         h = Math.min(Math.max(mh, h), mxh);
13873                         var tw = w;
13874                         w = ow * (h/oh);
13875                         x += tw - w;
13876                         break;
13877                     case "west":
13878                         var th = h;
13879                         h = oh * (w/ow);
13880                         h = Math.min(Math.max(mh, h), mxh);
13881                         y += (th - h) / 2;
13882                         var tw = w;
13883                         w = ow * (h/oh);
13884                         x += tw - w;
13885                        break;
13886                     case "northwest":
13887                         var tw = w;
13888                         var th = h;
13889                         h = oh * (w/ow);
13890                         h = Math.min(Math.max(mh, h), mxh);
13891                         w = ow * (h/oh);
13892                         y += th - h;
13893                         x += tw - w;
13894                        break;
13895
13896                 }
13897             }
13898             if (pos == 'hdrag') {
13899                 w = ow;
13900             }
13901             this.proxy.setBounds(x, y, w, h);
13902             if(this.dynamic){
13903                 this.resizeElement();
13904             }
13905             }catch(e){}
13906         }
13907         this.fireEvent("resizing", this, x, y, w, h, e);
13908     },
13909
13910     // private
13911     handleOver : function(){
13912         if(this.enabled){
13913             this.el.addClass("x-resizable-over");
13914         }
13915     },
13916
13917     // private
13918     handleOut : function(){
13919         if(!this.resizing){
13920             this.el.removeClass("x-resizable-over");
13921         }
13922     },
13923
13924     /**
13925      * Returns the element this component is bound to.
13926      * @return {Roo.Element}
13927      */
13928     getEl : function(){
13929         return this.el;
13930     },
13931
13932     /**
13933      * Returns the resizeChild element (or null).
13934      * @return {Roo.Element}
13935      */
13936     getResizeChild : function(){
13937         return this.resizeChild;
13938     },
13939     groupHandler : function()
13940     {
13941         
13942     },
13943     /**
13944      * Destroys this resizable. If the element was wrapped and
13945      * removeEl is not true then the element remains.
13946      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13947      */
13948     destroy : function(removeEl){
13949         this.proxy.remove();
13950         if(this.overlay){
13951             this.overlay.removeAllListeners();
13952             this.overlay.remove();
13953         }
13954         var ps = Roo.Resizable.positions;
13955         for(var k in ps){
13956             if(typeof ps[k] != "function" && this[ps[k]]){
13957                 var h = this[ps[k]];
13958                 h.el.removeAllListeners();
13959                 h.el.remove();
13960             }
13961         }
13962         if(removeEl){
13963             this.el.update("");
13964             this.el.remove();
13965         }
13966     }
13967 });
13968
13969 // private
13970 // hash to map config positions to true positions
13971 Roo.Resizable.positions = {
13972     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
13973     hd: "hdrag"
13974 };
13975
13976 // private
13977 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
13978     if(!this.tpl){
13979         // only initialize the template if resizable is used
13980         var tpl = Roo.DomHelper.createTemplate(
13981             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
13982         );
13983         tpl.compile();
13984         Roo.Resizable.Handle.prototype.tpl = tpl;
13985     }
13986     this.position = pos;
13987     this.rz = rz;
13988     // show north drag fro topdra
13989     var handlepos = pos == 'hdrag' ? 'north' : pos;
13990     
13991     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
13992     if (pos == 'hdrag') {
13993         this.el.setStyle('cursor', 'pointer');
13994     }
13995     this.el.unselectable();
13996     if(transparent){
13997         this.el.setOpacity(0);
13998     }
13999     this.el.on("mousedown", this.onMouseDown, this);
14000     if(!disableTrackOver){
14001         this.el.on("mouseover", this.onMouseOver, this);
14002         this.el.on("mouseout", this.onMouseOut, this);
14003     }
14004 };
14005
14006 // private
14007 Roo.Resizable.Handle.prototype = {
14008     afterResize : function(rz){
14009         // do nothing
14010     },
14011     // private
14012     onMouseDown : function(e){
14013         this.rz.onMouseDown(this, e);
14014     },
14015     // private
14016     onMouseOver : function(e){
14017         this.rz.handleOver(this, e);
14018     },
14019     // private
14020     onMouseOut : function(e){
14021         this.rz.handleOut(this, e);
14022     }
14023 };/*
14024  * Based on:
14025  * Ext JS Library 1.1.1
14026  * Copyright(c) 2006-2007, Ext JS, LLC.
14027  *
14028  * Originally Released Under LGPL - original licence link has changed is not relivant.
14029  *
14030  * Fork - LGPL
14031  * <script type="text/javascript">
14032  */
14033
14034 /**
14035  * @class Roo.Editor
14036  * @extends Roo.Component
14037  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
14038  * @constructor
14039  * Create a new Editor
14040  * @param {Roo.form.Field} field The Field object (or descendant)
14041  * @param {Object} config The config object
14042  */
14043 Roo.Editor = function(field, config){
14044     Roo.Editor.superclass.constructor.call(this, config);
14045     this.field = field;
14046     this.addEvents({
14047         /**
14048              * @event beforestartedit
14049              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
14050              * false from the handler of this event.
14051              * @param {Editor} this
14052              * @param {Roo.Element} boundEl The underlying element bound to this editor
14053              * @param {Mixed} value The field value being set
14054              */
14055         "beforestartedit" : true,
14056         /**
14057              * @event startedit
14058              * Fires when this editor is displayed
14059              * @param {Roo.Element} boundEl The underlying element bound to this editor
14060              * @param {Mixed} value The starting field value
14061              */
14062         "startedit" : true,
14063         /**
14064              * @event beforecomplete
14065              * Fires after a change has been made to the field, but before the change is reflected in the underlying
14066              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
14067              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
14068              * event will not fire since no edit actually occurred.
14069              * @param {Editor} this
14070              * @param {Mixed} value The current field value
14071              * @param {Mixed} startValue The original field value
14072              */
14073         "beforecomplete" : true,
14074         /**
14075              * @event complete
14076              * Fires after editing is complete and any changed value has been written to the underlying field.
14077              * @param {Editor} this
14078              * @param {Mixed} value The current field value
14079              * @param {Mixed} startValue The original field value
14080              */
14081         "complete" : true,
14082         /**
14083          * @event specialkey
14084          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
14085          * {@link Roo.EventObject#getKey} to determine which key was pressed.
14086          * @param {Roo.form.Field} this
14087          * @param {Roo.EventObject} e The event object
14088          */
14089         "specialkey" : true
14090     });
14091 };
14092
14093 Roo.extend(Roo.Editor, Roo.Component, {
14094     /**
14095      * @cfg {Boolean/String} autosize
14096      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
14097      * or "height" to adopt the height only (defaults to false)
14098      */
14099     /**
14100      * @cfg {Boolean} revertInvalid
14101      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
14102      * validation fails (defaults to true)
14103      */
14104     /**
14105      * @cfg {Boolean} ignoreNoChange
14106      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
14107      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
14108      * will never be ignored.
14109      */
14110     /**
14111      * @cfg {Boolean} hideEl
14112      * False to keep the bound element visible while the editor is displayed (defaults to true)
14113      */
14114     /**
14115      * @cfg {Mixed} value
14116      * The data value of the underlying field (defaults to "")
14117      */
14118     value : "",
14119     /**
14120      * @cfg {String} alignment
14121      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
14122      */
14123     alignment: "c-c?",
14124     /**
14125      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
14126      * for bottom-right shadow (defaults to "frame")
14127      */
14128     shadow : "frame",
14129     /**
14130      * @cfg {Boolean} constrain True to constrain the editor to the viewport
14131      */
14132     constrain : false,
14133     /**
14134      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
14135      */
14136     completeOnEnter : false,
14137     /**
14138      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
14139      */
14140     cancelOnEsc : false,
14141     /**
14142      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
14143      */
14144     updateEl : false,
14145
14146     // private
14147     onRender : function(ct, position){
14148         this.el = new Roo.Layer({
14149             shadow: this.shadow,
14150             cls: "x-editor",
14151             parentEl : ct,
14152             shim : this.shim,
14153             shadowOffset:4,
14154             id: this.id,
14155             constrain: this.constrain
14156         });
14157         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
14158         if(this.field.msgTarget != 'title'){
14159             this.field.msgTarget = 'qtip';
14160         }
14161         this.field.render(this.el);
14162         if(Roo.isGecko){
14163             this.field.el.dom.setAttribute('autocomplete', 'off');
14164         }
14165         this.field.on("specialkey", this.onSpecialKey, this);
14166         if(this.swallowKeys){
14167             this.field.el.swallowEvent(['keydown','keypress']);
14168         }
14169         this.field.show();
14170         this.field.on("blur", this.onBlur, this);
14171         if(this.field.grow){
14172             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
14173         }
14174     },
14175
14176     onSpecialKey : function(field, e)
14177     {
14178         //Roo.log('editor onSpecialKey');
14179         if(this.completeOnEnter && e.getKey() == e.ENTER){
14180             e.stopEvent();
14181             this.completeEdit();
14182             return;
14183         }
14184         // do not fire special key otherwise it might hide close the editor...
14185         if(e.getKey() == e.ENTER){    
14186             return;
14187         }
14188         if(this.cancelOnEsc && e.getKey() == e.ESC){
14189             this.cancelEdit();
14190             return;
14191         } 
14192         this.fireEvent('specialkey', field, e);
14193     
14194     },
14195
14196     /**
14197      * Starts the editing process and shows the editor.
14198      * @param {String/HTMLElement/Element} el The element to edit
14199      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
14200       * to the innerHTML of el.
14201      */
14202     startEdit : function(el, value){
14203         if(this.editing){
14204             this.completeEdit();
14205         }
14206         this.boundEl = Roo.get(el);
14207         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
14208         if(!this.rendered){
14209             this.render(this.parentEl || document.body);
14210         }
14211         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
14212             return;
14213         }
14214         this.startValue = v;
14215         this.field.setValue(v);
14216         if(this.autoSize){
14217             var sz = this.boundEl.getSize();
14218             switch(this.autoSize){
14219                 case "width":
14220                 this.setSize(sz.width,  "");
14221                 break;
14222                 case "height":
14223                 this.setSize("",  sz.height);
14224                 break;
14225                 default:
14226                 this.setSize(sz.width,  sz.height);
14227             }
14228         }
14229         this.el.alignTo(this.boundEl, this.alignment);
14230         this.editing = true;
14231         if(Roo.QuickTips){
14232             Roo.QuickTips.disable();
14233         }
14234         this.show();
14235     },
14236
14237     /**
14238      * Sets the height and width of this editor.
14239      * @param {Number} width The new width
14240      * @param {Number} height The new height
14241      */
14242     setSize : function(w, h){
14243         this.field.setSize(w, h);
14244         if(this.el){
14245             this.el.sync();
14246         }
14247     },
14248
14249     /**
14250      * Realigns the editor to the bound field based on the current alignment config value.
14251      */
14252     realign : function(){
14253         this.el.alignTo(this.boundEl, this.alignment);
14254     },
14255
14256     /**
14257      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
14258      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
14259      */
14260     completeEdit : function(remainVisible){
14261         if(!this.editing){
14262             return;
14263         }
14264         var v = this.getValue();
14265         if(this.revertInvalid !== false && !this.field.isValid()){
14266             v = this.startValue;
14267             this.cancelEdit(true);
14268         }
14269         if(String(v) === String(this.startValue) && this.ignoreNoChange){
14270             this.editing = false;
14271             this.hide();
14272             return;
14273         }
14274         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
14275             this.editing = false;
14276             if(this.updateEl && this.boundEl){
14277                 this.boundEl.update(v);
14278             }
14279             if(remainVisible !== true){
14280                 this.hide();
14281             }
14282             this.fireEvent("complete", this, v, this.startValue);
14283         }
14284     },
14285
14286     // private
14287     onShow : function(){
14288         this.el.show();
14289         if(this.hideEl !== false){
14290             this.boundEl.hide();
14291         }
14292         this.field.show();
14293         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
14294             this.fixIEFocus = true;
14295             this.deferredFocus.defer(50, this);
14296         }else{
14297             this.field.focus();
14298         }
14299         this.fireEvent("startedit", this.boundEl, this.startValue);
14300     },
14301
14302     deferredFocus : function(){
14303         if(this.editing){
14304             this.field.focus();
14305         }
14306     },
14307
14308     /**
14309      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
14310      * reverted to the original starting value.
14311      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
14312      * cancel (defaults to false)
14313      */
14314     cancelEdit : function(remainVisible){
14315         if(this.editing){
14316             this.setValue(this.startValue);
14317             if(remainVisible !== true){
14318                 this.hide();
14319             }
14320         }
14321     },
14322
14323     // private
14324     onBlur : function(){
14325         if(this.allowBlur !== true && this.editing){
14326             this.completeEdit();
14327         }
14328     },
14329
14330     // private
14331     onHide : function(){
14332         if(this.editing){
14333             this.completeEdit();
14334             return;
14335         }
14336         this.field.blur();
14337         if(this.field.collapse){
14338             this.field.collapse();
14339         }
14340         this.el.hide();
14341         if(this.hideEl !== false){
14342             this.boundEl.show();
14343         }
14344         if(Roo.QuickTips){
14345             Roo.QuickTips.enable();
14346         }
14347     },
14348
14349     /**
14350      * Sets the data value of the editor
14351      * @param {Mixed} value Any valid value supported by the underlying field
14352      */
14353     setValue : function(v){
14354         this.field.setValue(v);
14355     },
14356
14357     /**
14358      * Gets the data value of the editor
14359      * @return {Mixed} The data value
14360      */
14361     getValue : function(){
14362         return this.field.getValue();
14363     }
14364 });/*
14365  * Based on:
14366  * Ext JS Library 1.1.1
14367  * Copyright(c) 2006-2007, Ext JS, LLC.
14368  *
14369  * Originally Released Under LGPL - original licence link has changed is not relivant.
14370  *
14371  * Fork - LGPL
14372  * <script type="text/javascript">
14373  */
14374  
14375 /**
14376  * @class Roo.BasicDialog
14377  * @extends Roo.util.Observable
14378  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
14379  * <pre><code>
14380 var dlg = new Roo.BasicDialog("my-dlg", {
14381     height: 200,
14382     width: 300,
14383     minHeight: 100,
14384     minWidth: 150,
14385     modal: true,
14386     proxyDrag: true,
14387     shadow: true
14388 });
14389 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
14390 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
14391 dlg.addButton('Cancel', dlg.hide, dlg);
14392 dlg.show();
14393 </code></pre>
14394   <b>A Dialog should always be a direct child of the body element.</b>
14395  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
14396  * @cfg {String} title Default text to display in the title bar (defaults to null)
14397  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14398  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14399  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
14400  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
14401  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
14402  * (defaults to null with no animation)
14403  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
14404  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
14405  * property for valid values (defaults to 'all')
14406  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
14407  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
14408  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
14409  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
14410  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
14411  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
14412  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
14413  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
14414  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
14415  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
14416  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
14417  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
14418  * draggable = true (defaults to false)
14419  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
14420  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14421  * shadow (defaults to false)
14422  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
14423  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
14424  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
14425  * @cfg {Array} buttons Array of buttons
14426  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
14427  * @constructor
14428  * Create a new BasicDialog.
14429  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
14430  * @param {Object} config Configuration options
14431  */
14432 Roo.BasicDialog = function(el, config){
14433     this.el = Roo.get(el);
14434     var dh = Roo.DomHelper;
14435     if(!this.el && config && config.autoCreate){
14436         if(typeof config.autoCreate == "object"){
14437             if(!config.autoCreate.id){
14438                 config.autoCreate.id = el;
14439             }
14440             this.el = dh.append(document.body,
14441                         config.autoCreate, true);
14442         }else{
14443             this.el = dh.append(document.body,
14444                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
14445         }
14446     }
14447     el = this.el;
14448     el.setDisplayed(true);
14449     el.hide = this.hideAction;
14450     this.id = el.id;
14451     el.addClass("x-dlg");
14452
14453     Roo.apply(this, config);
14454
14455     this.proxy = el.createProxy("x-dlg-proxy");
14456     this.proxy.hide = this.hideAction;
14457     this.proxy.setOpacity(.5);
14458     this.proxy.hide();
14459
14460     if(config.width){
14461         el.setWidth(config.width);
14462     }
14463     if(config.height){
14464         el.setHeight(config.height);
14465     }
14466     this.size = el.getSize();
14467     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
14468         this.xy = [config.x,config.y];
14469     }else{
14470         this.xy = el.getCenterXY(true);
14471     }
14472     /** The header element @type Roo.Element */
14473     this.header = el.child("> .x-dlg-hd");
14474     /** The body element @type Roo.Element */
14475     this.body = el.child("> .x-dlg-bd");
14476     /** The footer element @type Roo.Element */
14477     this.footer = el.child("> .x-dlg-ft");
14478
14479     if(!this.header){
14480         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
14481     }
14482     if(!this.body){
14483         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
14484     }
14485
14486     this.header.unselectable();
14487     if(this.title){
14488         this.header.update(this.title);
14489     }
14490     // this element allows the dialog to be focused for keyboard event
14491     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
14492     this.focusEl.swallowEvent("click", true);
14493
14494     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
14495
14496     // wrap the body and footer for special rendering
14497     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
14498     if(this.footer){
14499         this.bwrap.dom.appendChild(this.footer.dom);
14500     }
14501
14502     this.bg = this.el.createChild({
14503         tag: "div", cls:"x-dlg-bg",
14504         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
14505     });
14506     this.centerBg = this.bg.child("div.x-dlg-bg-center");
14507
14508
14509     if(this.autoScroll !== false && !this.autoTabs){
14510         this.body.setStyle("overflow", "auto");
14511     }
14512
14513     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
14514
14515     if(this.closable !== false){
14516         this.el.addClass("x-dlg-closable");
14517         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
14518         this.close.on("click", this.closeClick, this);
14519         this.close.addClassOnOver("x-dlg-close-over");
14520     }
14521     if(this.collapsible !== false){
14522         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
14523         this.collapseBtn.on("click", this.collapseClick, this);
14524         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
14525         this.header.on("dblclick", this.collapseClick, this);
14526     }
14527     if(this.resizable !== false){
14528         this.el.addClass("x-dlg-resizable");
14529         this.resizer = new Roo.Resizable(el, {
14530             minWidth: this.minWidth || 80,
14531             minHeight:this.minHeight || 80,
14532             handles: this.resizeHandles || "all",
14533             pinned: true
14534         });
14535         this.resizer.on("beforeresize", this.beforeResize, this);
14536         this.resizer.on("resize", this.onResize, this);
14537     }
14538     if(this.draggable !== false){
14539         el.addClass("x-dlg-draggable");
14540         if (!this.proxyDrag) {
14541             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
14542         }
14543         else {
14544             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
14545         }
14546         dd.setHandleElId(this.header.id);
14547         dd.endDrag = this.endMove.createDelegate(this);
14548         dd.startDrag = this.startMove.createDelegate(this);
14549         dd.onDrag = this.onDrag.createDelegate(this);
14550         dd.scroll = false;
14551         this.dd = dd;
14552     }
14553     if(this.modal){
14554         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
14555         this.mask.enableDisplayMode("block");
14556         this.mask.hide();
14557         this.el.addClass("x-dlg-modal");
14558     }
14559     if(this.shadow){
14560         this.shadow = new Roo.Shadow({
14561             mode : typeof this.shadow == "string" ? this.shadow : "sides",
14562             offset : this.shadowOffset
14563         });
14564     }else{
14565         this.shadowOffset = 0;
14566     }
14567     if(Roo.useShims && this.shim !== false){
14568         this.shim = this.el.createShim();
14569         this.shim.hide = this.hideAction;
14570         this.shim.hide();
14571     }else{
14572         this.shim = false;
14573     }
14574     if(this.autoTabs){
14575         this.initTabs();
14576     }
14577     if (this.buttons) { 
14578         var bts= this.buttons;
14579         this.buttons = [];
14580         Roo.each(bts, function(b) {
14581             this.addButton(b);
14582         }, this);
14583     }
14584     
14585     
14586     this.addEvents({
14587         /**
14588          * @event keydown
14589          * Fires when a key is pressed
14590          * @param {Roo.BasicDialog} this
14591          * @param {Roo.EventObject} e
14592          */
14593         "keydown" : true,
14594         /**
14595          * @event move
14596          * Fires when this dialog is moved by the user.
14597          * @param {Roo.BasicDialog} this
14598          * @param {Number} x The new page X
14599          * @param {Number} y The new page Y
14600          */
14601         "move" : true,
14602         /**
14603          * @event resize
14604          * Fires when this dialog is resized by the user.
14605          * @param {Roo.BasicDialog} this
14606          * @param {Number} width The new width
14607          * @param {Number} height The new height
14608          */
14609         "resize" : true,
14610         /**
14611          * @event beforehide
14612          * Fires before this dialog is hidden.
14613          * @param {Roo.BasicDialog} this
14614          */
14615         "beforehide" : true,
14616         /**
14617          * @event hide
14618          * Fires when this dialog is hidden.
14619          * @param {Roo.BasicDialog} this
14620          */
14621         "hide" : true,
14622         /**
14623          * @event beforeshow
14624          * Fires before this dialog is shown.
14625          * @param {Roo.BasicDialog} this
14626          */
14627         "beforeshow" : true,
14628         /**
14629          * @event show
14630          * Fires when this dialog is shown.
14631          * @param {Roo.BasicDialog} this
14632          */
14633         "show" : true
14634     });
14635     el.on("keydown", this.onKeyDown, this);
14636     el.on("mousedown", this.toFront, this);
14637     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
14638     this.el.hide();
14639     Roo.DialogManager.register(this);
14640     Roo.BasicDialog.superclass.constructor.call(this);
14641 };
14642
14643 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
14644     shadowOffset: Roo.isIE ? 6 : 5,
14645     minHeight: 80,
14646     minWidth: 200,
14647     minButtonWidth: 75,
14648     defaultButton: null,
14649     buttonAlign: "right",
14650     tabTag: 'div',
14651     firstShow: true,
14652
14653     /**
14654      * Sets the dialog title text
14655      * @param {String} text The title text to display
14656      * @return {Roo.BasicDialog} this
14657      */
14658     setTitle : function(text){
14659         this.header.update(text);
14660         return this;
14661     },
14662
14663     // private
14664     closeClick : function(){
14665         this.hide();
14666     },
14667
14668     // private
14669     collapseClick : function(){
14670         this[this.collapsed ? "expand" : "collapse"]();
14671     },
14672
14673     /**
14674      * Collapses the dialog to its minimized state (only the title bar is visible).
14675      * Equivalent to the user clicking the collapse dialog button.
14676      */
14677     collapse : function(){
14678         if(!this.collapsed){
14679             this.collapsed = true;
14680             this.el.addClass("x-dlg-collapsed");
14681             this.restoreHeight = this.el.getHeight();
14682             this.resizeTo(this.el.getWidth(), this.header.getHeight());
14683         }
14684     },
14685
14686     /**
14687      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
14688      * clicking the expand dialog button.
14689      */
14690     expand : function(){
14691         if(this.collapsed){
14692             this.collapsed = false;
14693             this.el.removeClass("x-dlg-collapsed");
14694             this.resizeTo(this.el.getWidth(), this.restoreHeight);
14695         }
14696     },
14697
14698     /**
14699      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14700      * @return {Roo.TabPanel} The tabs component
14701      */
14702     initTabs : function(){
14703         var tabs = this.getTabs();
14704         while(tabs.getTab(0)){
14705             tabs.removeTab(0);
14706         }
14707         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14708             var dom = el.dom;
14709             tabs.addTab(Roo.id(dom), dom.title);
14710             dom.title = "";
14711         });
14712         tabs.activate(0);
14713         return tabs;
14714     },
14715
14716     // private
14717     beforeResize : function(){
14718         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14719     },
14720
14721     // private
14722     onResize : function(){
14723         this.refreshSize();
14724         this.syncBodyHeight();
14725         this.adjustAssets();
14726         this.focus();
14727         this.fireEvent("resize", this, this.size.width, this.size.height);
14728     },
14729
14730     // private
14731     onKeyDown : function(e){
14732         if(this.isVisible()){
14733             this.fireEvent("keydown", this, e);
14734         }
14735     },
14736
14737     /**
14738      * Resizes the dialog.
14739      * @param {Number} width
14740      * @param {Number} height
14741      * @return {Roo.BasicDialog} this
14742      */
14743     resizeTo : function(width, height){
14744         this.el.setSize(width, height);
14745         this.size = {width: width, height: height};
14746         this.syncBodyHeight();
14747         if(this.fixedcenter){
14748             this.center();
14749         }
14750         if(this.isVisible()){
14751             this.constrainXY();
14752             this.adjustAssets();
14753         }
14754         this.fireEvent("resize", this, width, height);
14755         return this;
14756     },
14757
14758
14759     /**
14760      * Resizes the dialog to fit the specified content size.
14761      * @param {Number} width
14762      * @param {Number} height
14763      * @return {Roo.BasicDialog} this
14764      */
14765     setContentSize : function(w, h){
14766         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14767         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14768         //if(!this.el.isBorderBox()){
14769             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14770             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14771         //}
14772         if(this.tabs){
14773             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14774             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14775         }
14776         this.resizeTo(w, h);
14777         return this;
14778     },
14779
14780     /**
14781      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
14782      * executed in response to a particular key being pressed while the dialog is active.
14783      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14784      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14785      * @param {Function} fn The function to call
14786      * @param {Object} scope (optional) The scope of the function
14787      * @return {Roo.BasicDialog} this
14788      */
14789     addKeyListener : function(key, fn, scope){
14790         var keyCode, shift, ctrl, alt;
14791         if(typeof key == "object" && !(key instanceof Array)){
14792             keyCode = key["key"];
14793             shift = key["shift"];
14794             ctrl = key["ctrl"];
14795             alt = key["alt"];
14796         }else{
14797             keyCode = key;
14798         }
14799         var handler = function(dlg, e){
14800             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14801                 var k = e.getKey();
14802                 if(keyCode instanceof Array){
14803                     for(var i = 0, len = keyCode.length; i < len; i++){
14804                         if(keyCode[i] == k){
14805                           fn.call(scope || window, dlg, k, e);
14806                           return;
14807                         }
14808                     }
14809                 }else{
14810                     if(k == keyCode){
14811                         fn.call(scope || window, dlg, k, e);
14812                     }
14813                 }
14814             }
14815         };
14816         this.on("keydown", handler);
14817         return this;
14818     },
14819
14820     /**
14821      * Returns the TabPanel component (creates it if it doesn't exist).
14822      * Note: If you wish to simply check for the existence of tabs without creating them,
14823      * check for a null 'tabs' property.
14824      * @return {Roo.TabPanel} The tabs component
14825      */
14826     getTabs : function(){
14827         if(!this.tabs){
14828             this.el.addClass("x-dlg-auto-tabs");
14829             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14830             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14831         }
14832         return this.tabs;
14833     },
14834
14835     /**
14836      * Adds a button to the footer section of the dialog.
14837      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14838      * object or a valid Roo.DomHelper element config
14839      * @param {Function} handler The function called when the button is clicked
14840      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14841      * @return {Roo.Button} The new button
14842      */
14843     addButton : function(config, handler, scope){
14844         var dh = Roo.DomHelper;
14845         if(!this.footer){
14846             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14847         }
14848         if(!this.btnContainer){
14849             var tb = this.footer.createChild({
14850
14851                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14852                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14853             }, null, true);
14854             this.btnContainer = tb.firstChild.firstChild.firstChild;
14855         }
14856         var bconfig = {
14857             handler: handler,
14858             scope: scope,
14859             minWidth: this.minButtonWidth,
14860             hideParent:true
14861         };
14862         if(typeof config == "string"){
14863             bconfig.text = config;
14864         }else{
14865             if(config.tag){
14866                 bconfig.dhconfig = config;
14867             }else{
14868                 Roo.apply(bconfig, config);
14869             }
14870         }
14871         var fc = false;
14872         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14873             bconfig.position = Math.max(0, bconfig.position);
14874             fc = this.btnContainer.childNodes[bconfig.position];
14875         }
14876          
14877         var btn = new Roo.Button(
14878             fc ? 
14879                 this.btnContainer.insertBefore(document.createElement("td"),fc)
14880                 : this.btnContainer.appendChild(document.createElement("td")),
14881             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
14882             bconfig
14883         );
14884         this.syncBodyHeight();
14885         if(!this.buttons){
14886             /**
14887              * Array of all the buttons that have been added to this dialog via addButton
14888              * @type Array
14889              */
14890             this.buttons = [];
14891         }
14892         this.buttons.push(btn);
14893         return btn;
14894     },
14895
14896     /**
14897      * Sets the default button to be focused when the dialog is displayed.
14898      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14899      * @return {Roo.BasicDialog} this
14900      */
14901     setDefaultButton : function(btn){
14902         this.defaultButton = btn;
14903         return this;
14904     },
14905
14906     // private
14907     getHeaderFooterHeight : function(safe){
14908         var height = 0;
14909         if(this.header){
14910            height += this.header.getHeight();
14911         }
14912         if(this.footer){
14913            var fm = this.footer.getMargins();
14914             height += (this.footer.getHeight()+fm.top+fm.bottom);
14915         }
14916         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14917         height += this.centerBg.getPadding("tb");
14918         return height;
14919     },
14920
14921     // private
14922     syncBodyHeight : function()
14923     {
14924         var bd = this.body, // the text
14925             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
14926             bw = this.bwrap;
14927         var height = this.size.height - this.getHeaderFooterHeight(false);
14928         bd.setHeight(height-bd.getMargins("tb"));
14929         var hh = this.header.getHeight();
14930         var h = this.size.height-hh;
14931         cb.setHeight(h);
14932         
14933         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14934         bw.setHeight(h-cb.getPadding("tb"));
14935         
14936         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14937         bd.setWidth(bw.getWidth(true));
14938         if(this.tabs){
14939             this.tabs.syncHeight();
14940             if(Roo.isIE){
14941                 this.tabs.el.repaint();
14942             }
14943         }
14944     },
14945
14946     /**
14947      * Restores the previous state of the dialog if Roo.state is configured.
14948      * @return {Roo.BasicDialog} this
14949      */
14950     restoreState : function(){
14951         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14952         if(box && box.width){
14953             this.xy = [box.x, box.y];
14954             this.resizeTo(box.width, box.height);
14955         }
14956         return this;
14957     },
14958
14959     // private
14960     beforeShow : function(){
14961         this.expand();
14962         if(this.fixedcenter){
14963             this.xy = this.el.getCenterXY(true);
14964         }
14965         if(this.modal){
14966             Roo.get(document.body).addClass("x-body-masked");
14967             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14968             this.mask.show();
14969         }
14970         this.constrainXY();
14971     },
14972
14973     // private
14974     animShow : function(){
14975         var b = Roo.get(this.animateTarget).getBox();
14976         this.proxy.setSize(b.width, b.height);
14977         this.proxy.setLocation(b.x, b.y);
14978         this.proxy.show();
14979         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
14980                     true, .35, this.showEl.createDelegate(this));
14981     },
14982
14983     /**
14984      * Shows the dialog.
14985      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
14986      * @return {Roo.BasicDialog} this
14987      */
14988     show : function(animateTarget){
14989         if (this.fireEvent("beforeshow", this) === false){
14990             return;
14991         }
14992         if(this.syncHeightBeforeShow){
14993             this.syncBodyHeight();
14994         }else if(this.firstShow){
14995             this.firstShow = false;
14996             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
14997         }
14998         this.animateTarget = animateTarget || this.animateTarget;
14999         if(!this.el.isVisible()){
15000             this.beforeShow();
15001             if(this.animateTarget && Roo.get(this.animateTarget)){
15002                 this.animShow();
15003             }else{
15004                 this.showEl();
15005             }
15006         }
15007         return this;
15008     },
15009
15010     // private
15011     showEl : function(){
15012         this.proxy.hide();
15013         this.el.setXY(this.xy);
15014         this.el.show();
15015         this.adjustAssets(true);
15016         this.toFront();
15017         this.focus();
15018         // IE peekaboo bug - fix found by Dave Fenwick
15019         if(Roo.isIE){
15020             this.el.repaint();
15021         }
15022         this.fireEvent("show", this);
15023     },
15024
15025     /**
15026      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
15027      * dialog itself will receive focus.
15028      */
15029     focus : function(){
15030         if(this.defaultButton){
15031             this.defaultButton.focus();
15032         }else{
15033             this.focusEl.focus();
15034         }
15035     },
15036
15037     // private
15038     constrainXY : function(){
15039         if(this.constraintoviewport !== false){
15040             if(!this.viewSize){
15041                 if(this.container){
15042                     var s = this.container.getSize();
15043                     this.viewSize = [s.width, s.height];
15044                 }else{
15045                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
15046                 }
15047             }
15048             var s = Roo.get(this.container||document).getScroll();
15049
15050             var x = this.xy[0], y = this.xy[1];
15051             var w = this.size.width, h = this.size.height;
15052             var vw = this.viewSize[0], vh = this.viewSize[1];
15053             // only move it if it needs it
15054             var moved = false;
15055             // first validate right/bottom
15056             if(x + w > vw+s.left){
15057                 x = vw - w;
15058                 moved = true;
15059             }
15060             if(y + h > vh+s.top){
15061                 y = vh - h;
15062                 moved = true;
15063             }
15064             // then make sure top/left isn't negative
15065             if(x < s.left){
15066                 x = s.left;
15067                 moved = true;
15068             }
15069             if(y < s.top){
15070                 y = s.top;
15071                 moved = true;
15072             }
15073             if(moved){
15074                 // cache xy
15075                 this.xy = [x, y];
15076                 if(this.isVisible()){
15077                     this.el.setLocation(x, y);
15078                     this.adjustAssets();
15079                 }
15080             }
15081         }
15082     },
15083
15084     // private
15085     onDrag : function(){
15086         if(!this.proxyDrag){
15087             this.xy = this.el.getXY();
15088             this.adjustAssets();
15089         }
15090     },
15091
15092     // private
15093     adjustAssets : function(doShow){
15094         var x = this.xy[0], y = this.xy[1];
15095         var w = this.size.width, h = this.size.height;
15096         if(doShow === true){
15097             if(this.shadow){
15098                 this.shadow.show(this.el);
15099             }
15100             if(this.shim){
15101                 this.shim.show();
15102             }
15103         }
15104         if(this.shadow && this.shadow.isVisible()){
15105             this.shadow.show(this.el);
15106         }
15107         if(this.shim && this.shim.isVisible()){
15108             this.shim.setBounds(x, y, w, h);
15109         }
15110     },
15111
15112     // private
15113     adjustViewport : function(w, h){
15114         if(!w || !h){
15115             w = Roo.lib.Dom.getViewWidth();
15116             h = Roo.lib.Dom.getViewHeight();
15117         }
15118         // cache the size
15119         this.viewSize = [w, h];
15120         if(this.modal && this.mask.isVisible()){
15121             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
15122             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15123         }
15124         if(this.isVisible()){
15125             this.constrainXY();
15126         }
15127     },
15128
15129     /**
15130      * Destroys this dialog and all its supporting elements (including any tabs, shim,
15131      * shadow, proxy, mask, etc.)  Also removes all event listeners.
15132      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
15133      */
15134     destroy : function(removeEl){
15135         if(this.isVisible()){
15136             this.animateTarget = null;
15137             this.hide();
15138         }
15139         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
15140         if(this.tabs){
15141             this.tabs.destroy(removeEl);
15142         }
15143         Roo.destroy(
15144              this.shim,
15145              this.proxy,
15146              this.resizer,
15147              this.close,
15148              this.mask
15149         );
15150         if(this.dd){
15151             this.dd.unreg();
15152         }
15153         if(this.buttons){
15154            for(var i = 0, len = this.buttons.length; i < len; i++){
15155                this.buttons[i].destroy();
15156            }
15157         }
15158         this.el.removeAllListeners();
15159         if(removeEl === true){
15160             this.el.update("");
15161             this.el.remove();
15162         }
15163         Roo.DialogManager.unregister(this);
15164     },
15165
15166     // private
15167     startMove : function(){
15168         if(this.proxyDrag){
15169             this.proxy.show();
15170         }
15171         if(this.constraintoviewport !== false){
15172             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
15173         }
15174     },
15175
15176     // private
15177     endMove : function(){
15178         if(!this.proxyDrag){
15179             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
15180         }else{
15181             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
15182             this.proxy.hide();
15183         }
15184         this.refreshSize();
15185         this.adjustAssets();
15186         this.focus();
15187         this.fireEvent("move", this, this.xy[0], this.xy[1]);
15188     },
15189
15190     /**
15191      * Brings this dialog to the front of any other visible dialogs
15192      * @return {Roo.BasicDialog} this
15193      */
15194     toFront : function(){
15195         Roo.DialogManager.bringToFront(this);
15196         return this;
15197     },
15198
15199     /**
15200      * Sends this dialog to the back (under) of any other visible dialogs
15201      * @return {Roo.BasicDialog} this
15202      */
15203     toBack : function(){
15204         Roo.DialogManager.sendToBack(this);
15205         return this;
15206     },
15207
15208     /**
15209      * Centers this dialog in the viewport
15210      * @return {Roo.BasicDialog} this
15211      */
15212     center : function(){
15213         var xy = this.el.getCenterXY(true);
15214         this.moveTo(xy[0], xy[1]);
15215         return this;
15216     },
15217
15218     /**
15219      * Moves the dialog's top-left corner to the specified point
15220      * @param {Number} x
15221      * @param {Number} y
15222      * @return {Roo.BasicDialog} this
15223      */
15224     moveTo : function(x, y){
15225         this.xy = [x,y];
15226         if(this.isVisible()){
15227             this.el.setXY(this.xy);
15228             this.adjustAssets();
15229         }
15230         return this;
15231     },
15232
15233     /**
15234      * Aligns the dialog to the specified element
15235      * @param {String/HTMLElement/Roo.Element} element The element to align to.
15236      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
15237      * @param {Array} offsets (optional) Offset the positioning by [x, y]
15238      * @return {Roo.BasicDialog} this
15239      */
15240     alignTo : function(element, position, offsets){
15241         this.xy = this.el.getAlignToXY(element, position, offsets);
15242         if(this.isVisible()){
15243             this.el.setXY(this.xy);
15244             this.adjustAssets();
15245         }
15246         return this;
15247     },
15248
15249     /**
15250      * Anchors an element to another element and realigns it when the window is resized.
15251      * @param {String/HTMLElement/Roo.Element} element The element to align to.
15252      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
15253      * @param {Array} offsets (optional) Offset the positioning by [x, y]
15254      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
15255      * is a number, it is used as the buffer delay (defaults to 50ms).
15256      * @return {Roo.BasicDialog} this
15257      */
15258     anchorTo : function(el, alignment, offsets, monitorScroll){
15259         var action = function(){
15260             this.alignTo(el, alignment, offsets);
15261         };
15262         Roo.EventManager.onWindowResize(action, this);
15263         var tm = typeof monitorScroll;
15264         if(tm != 'undefined'){
15265             Roo.EventManager.on(window, 'scroll', action, this,
15266                 {buffer: tm == 'number' ? monitorScroll : 50});
15267         }
15268         action.call(this);
15269         return this;
15270     },
15271
15272     /**
15273      * Returns true if the dialog is visible
15274      * @return {Boolean}
15275      */
15276     isVisible : function(){
15277         return this.el.isVisible();
15278     },
15279
15280     // private
15281     animHide : function(callback){
15282         var b = Roo.get(this.animateTarget).getBox();
15283         this.proxy.show();
15284         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
15285         this.el.hide();
15286         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
15287                     this.hideEl.createDelegate(this, [callback]));
15288     },
15289
15290     /**
15291      * Hides the dialog.
15292      * @param {Function} callback (optional) Function to call when the dialog is hidden
15293      * @return {Roo.BasicDialog} this
15294      */
15295     hide : function(callback){
15296         if (this.fireEvent("beforehide", this) === false){
15297             return;
15298         }
15299         if(this.shadow){
15300             this.shadow.hide();
15301         }
15302         if(this.shim) {
15303           this.shim.hide();
15304         }
15305         // sometimes animateTarget seems to get set.. causing problems...
15306         // this just double checks..
15307         if(this.animateTarget && Roo.get(this.animateTarget)) {
15308            this.animHide(callback);
15309         }else{
15310             this.el.hide();
15311             this.hideEl(callback);
15312         }
15313         return this;
15314     },
15315
15316     // private
15317     hideEl : function(callback){
15318         this.proxy.hide();
15319         if(this.modal){
15320             this.mask.hide();
15321             Roo.get(document.body).removeClass("x-body-masked");
15322         }
15323         this.fireEvent("hide", this);
15324         if(typeof callback == "function"){
15325             callback();
15326         }
15327     },
15328
15329     // private
15330     hideAction : function(){
15331         this.setLeft("-10000px");
15332         this.setTop("-10000px");
15333         this.setStyle("visibility", "hidden");
15334     },
15335
15336     // private
15337     refreshSize : function(){
15338         this.size = this.el.getSize();
15339         this.xy = this.el.getXY();
15340         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
15341     },
15342
15343     // private
15344     // z-index is managed by the DialogManager and may be overwritten at any time
15345     setZIndex : function(index){
15346         if(this.modal){
15347             this.mask.setStyle("z-index", index);
15348         }
15349         if(this.shim){
15350             this.shim.setStyle("z-index", ++index);
15351         }
15352         if(this.shadow){
15353             this.shadow.setZIndex(++index);
15354         }
15355         this.el.setStyle("z-index", ++index);
15356         if(this.proxy){
15357             this.proxy.setStyle("z-index", ++index);
15358         }
15359         if(this.resizer){
15360             this.resizer.proxy.setStyle("z-index", ++index);
15361         }
15362
15363         this.lastZIndex = index;
15364     },
15365
15366     /**
15367      * Returns the element for this dialog
15368      * @return {Roo.Element} The underlying dialog Element
15369      */
15370     getEl : function(){
15371         return this.el;
15372     }
15373 });
15374
15375 /**
15376  * @class Roo.DialogManager
15377  * Provides global access to BasicDialogs that have been created and
15378  * support for z-indexing (layering) multiple open dialogs.
15379  */
15380 Roo.DialogManager = function(){
15381     var list = {};
15382     var accessList = [];
15383     var front = null;
15384
15385     // private
15386     var sortDialogs = function(d1, d2){
15387         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
15388     };
15389
15390     // private
15391     var orderDialogs = function(){
15392         accessList.sort(sortDialogs);
15393         var seed = Roo.DialogManager.zseed;
15394         for(var i = 0, len = accessList.length; i < len; i++){
15395             var dlg = accessList[i];
15396             if(dlg){
15397                 dlg.setZIndex(seed + (i*10));
15398             }
15399         }
15400     };
15401
15402     return {
15403         /**
15404          * The starting z-index for BasicDialogs (defaults to 9000)
15405          * @type Number The z-index value
15406          */
15407         zseed : 9000,
15408
15409         // private
15410         register : function(dlg){
15411             list[dlg.id] = dlg;
15412             accessList.push(dlg);
15413         },
15414
15415         // private
15416         unregister : function(dlg){
15417             delete list[dlg.id];
15418             var i=0;
15419             var len=0;
15420             if(!accessList.indexOf){
15421                 for(  i = 0, len = accessList.length; i < len; i++){
15422                     if(accessList[i] == dlg){
15423                         accessList.splice(i, 1);
15424                         return;
15425                     }
15426                 }
15427             }else{
15428                  i = accessList.indexOf(dlg);
15429                 if(i != -1){
15430                     accessList.splice(i, 1);
15431                 }
15432             }
15433         },
15434
15435         /**
15436          * Gets a registered dialog by id
15437          * @param {String/Object} id The id of the dialog or a dialog
15438          * @return {Roo.BasicDialog} this
15439          */
15440         get : function(id){
15441             return typeof id == "object" ? id : list[id];
15442         },
15443
15444         /**
15445          * Brings the specified dialog to the front
15446          * @param {String/Object} dlg The id of the dialog or a dialog
15447          * @return {Roo.BasicDialog} this
15448          */
15449         bringToFront : function(dlg){
15450             dlg = this.get(dlg);
15451             if(dlg != front){
15452                 front = dlg;
15453                 dlg._lastAccess = new Date().getTime();
15454                 orderDialogs();
15455             }
15456             return dlg;
15457         },
15458
15459         /**
15460          * Sends the specified dialog to the back
15461          * @param {String/Object} dlg The id of the dialog or a dialog
15462          * @return {Roo.BasicDialog} this
15463          */
15464         sendToBack : function(dlg){
15465             dlg = this.get(dlg);
15466             dlg._lastAccess = -(new Date().getTime());
15467             orderDialogs();
15468             return dlg;
15469         },
15470
15471         /**
15472          * Hides all dialogs
15473          */
15474         hideAll : function(){
15475             for(var id in list){
15476                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
15477                     list[id].hide();
15478                 }
15479             }
15480         }
15481     };
15482 }();
15483
15484 /**
15485  * @class Roo.LayoutDialog
15486  * @extends Roo.BasicDialog
15487  * Dialog which provides adjustments for working with a layout in a Dialog.
15488  * Add your necessary layout config options to the dialog's config.<br>
15489  * Example usage (including a nested layout):
15490  * <pre><code>
15491 if(!dialog){
15492     dialog = new Roo.LayoutDialog("download-dlg", {
15493         modal: true,
15494         width:600,
15495         height:450,
15496         shadow:true,
15497         minWidth:500,
15498         minHeight:350,
15499         autoTabs:true,
15500         proxyDrag:true,
15501         // layout config merges with the dialog config
15502         center:{
15503             tabPosition: "top",
15504             alwaysShowTabs: true
15505         }
15506     });
15507     dialog.addKeyListener(27, dialog.hide, dialog);
15508     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
15509     dialog.addButton("Build It!", this.getDownload, this);
15510
15511     // we can even add nested layouts
15512     var innerLayout = new Roo.BorderLayout("dl-inner", {
15513         east: {
15514             initialSize: 200,
15515             autoScroll:true,
15516             split:true
15517         },
15518         center: {
15519             autoScroll:true
15520         }
15521     });
15522     innerLayout.beginUpdate();
15523     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
15524     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
15525     innerLayout.endUpdate(true);
15526
15527     var layout = dialog.getLayout();
15528     layout.beginUpdate();
15529     layout.add("center", new Roo.ContentPanel("standard-panel",
15530                         {title: "Download the Source", fitToFrame:true}));
15531     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
15532                {title: "Build your own roo.js"}));
15533     layout.getRegion("center").showPanel(sp);
15534     layout.endUpdate();
15535 }
15536 </code></pre>
15537     * @constructor
15538     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
15539     * @param {Object} config configuration options
15540   */
15541 Roo.LayoutDialog = function(el, cfg){
15542     
15543     var config=  cfg;
15544     if (typeof(cfg) == 'undefined') {
15545         config = Roo.apply({}, el);
15546         // not sure why we use documentElement here.. - it should always be body.
15547         // IE7 borks horribly if we use documentElement.
15548         // webkit also does not like documentElement - it creates a body element...
15549         el = Roo.get( document.body || document.documentElement ).createChild();
15550         //config.autoCreate = true;
15551     }
15552     
15553     
15554     config.autoTabs = false;
15555     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
15556     this.body.setStyle({overflow:"hidden", position:"relative"});
15557     this.layout = new Roo.BorderLayout(this.body.dom, config);
15558     this.layout.monitorWindowResize = false;
15559     this.el.addClass("x-dlg-auto-layout");
15560     // fix case when center region overwrites center function
15561     this.center = Roo.BasicDialog.prototype.center;
15562     this.on("show", this.layout.layout, this.layout, true);
15563     if (config.items) {
15564         var xitems = config.items;
15565         delete config.items;
15566         Roo.each(xitems, this.addxtype, this);
15567     }
15568     
15569     
15570 };
15571 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
15572     /**
15573      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
15574      * @deprecated
15575      */
15576     endUpdate : function(){
15577         this.layout.endUpdate();
15578     },
15579
15580     /**
15581      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
15582      *  @deprecated
15583      */
15584     beginUpdate : function(){
15585         this.layout.beginUpdate();
15586     },
15587
15588     /**
15589      * Get the BorderLayout for this dialog
15590      * @return {Roo.BorderLayout}
15591      */
15592     getLayout : function(){
15593         return this.layout;
15594     },
15595
15596     showEl : function(){
15597         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
15598         if(Roo.isIE7){
15599             this.layout.layout();
15600         }
15601     },
15602
15603     // private
15604     // Use the syncHeightBeforeShow config option to control this automatically
15605     syncBodyHeight : function(){
15606         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
15607         if(this.layout){this.layout.layout();}
15608     },
15609     
15610       /**
15611      * Add an xtype element (actually adds to the layout.)
15612      * @return {Object} xdata xtype object data.
15613      */
15614     
15615     addxtype : function(c) {
15616         return this.layout.addxtype(c);
15617     }
15618 });/*
15619  * Based on:
15620  * Ext JS Library 1.1.1
15621  * Copyright(c) 2006-2007, Ext JS, LLC.
15622  *
15623  * Originally Released Under LGPL - original licence link has changed is not relivant.
15624  *
15625  * Fork - LGPL
15626  * <script type="text/javascript">
15627  */
15628  
15629 /**
15630  * @class Roo.MessageBox
15631  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
15632  * Example usage:
15633  *<pre><code>
15634 // Basic alert:
15635 Roo.Msg.alert('Status', 'Changes saved successfully.');
15636
15637 // Prompt for user data:
15638 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
15639     if (btn == 'ok'){
15640         // process text value...
15641     }
15642 });
15643
15644 // Show a dialog using config options:
15645 Roo.Msg.show({
15646    title:'Save Changes?',
15647    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
15648    buttons: Roo.Msg.YESNOCANCEL,
15649    fn: processResult,
15650    animEl: 'elId'
15651 });
15652 </code></pre>
15653  * @singleton
15654  */
15655 Roo.MessageBox = function(){
15656     var dlg, opt, mask, waitTimer;
15657     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
15658     var buttons, activeTextEl, bwidth;
15659
15660     // private
15661     var handleButton = function(button){
15662         dlg.hide();
15663         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
15664     };
15665
15666     // private
15667     var handleHide = function(){
15668         if(opt && opt.cls){
15669             dlg.el.removeClass(opt.cls);
15670         }
15671         if(waitTimer){
15672             Roo.TaskMgr.stop(waitTimer);
15673             waitTimer = null;
15674         }
15675     };
15676
15677     // private
15678     var updateButtons = function(b){
15679         var width = 0;
15680         if(!b){
15681             buttons["ok"].hide();
15682             buttons["cancel"].hide();
15683             buttons["yes"].hide();
15684             buttons["no"].hide();
15685             dlg.footer.dom.style.display = 'none';
15686             return width;
15687         }
15688         dlg.footer.dom.style.display = '';
15689         for(var k in buttons){
15690             if(typeof buttons[k] != "function"){
15691                 if(b[k]){
15692                     buttons[k].show();
15693                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15694                     width += buttons[k].el.getWidth()+15;
15695                 }else{
15696                     buttons[k].hide();
15697                 }
15698             }
15699         }
15700         return width;
15701     };
15702
15703     // private
15704     var handleEsc = function(d, k, e){
15705         if(opt && opt.closable !== false){
15706             dlg.hide();
15707         }
15708         if(e){
15709             e.stopEvent();
15710         }
15711     };
15712
15713     return {
15714         /**
15715          * Returns a reference to the underlying {@link Roo.BasicDialog} element
15716          * @return {Roo.BasicDialog} The BasicDialog element
15717          */
15718         getDialog : function(){
15719            if(!dlg){
15720                 dlg = new Roo.BasicDialog("x-msg-box", {
15721                     autoCreate : true,
15722                     shadow: true,
15723                     draggable: true,
15724                     resizable:false,
15725                     constraintoviewport:false,
15726                     fixedcenter:true,
15727                     collapsible : false,
15728                     shim:true,
15729                     modal: true,
15730                     width:400, height:100,
15731                     buttonAlign:"center",
15732                     closeClick : function(){
15733                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15734                             handleButton("no");
15735                         }else{
15736                             handleButton("cancel");
15737                         }
15738                     }
15739                 });
15740                 dlg.on("hide", handleHide);
15741                 mask = dlg.mask;
15742                 dlg.addKeyListener(27, handleEsc);
15743                 buttons = {};
15744                 var bt = this.buttonText;
15745                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15746                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15747                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15748                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15749                 bodyEl = dlg.body.createChild({
15750
15751                     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>'
15752                 });
15753                 msgEl = bodyEl.dom.firstChild;
15754                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15755                 textboxEl.enableDisplayMode();
15756                 textboxEl.addKeyListener([10,13], function(){
15757                     if(dlg.isVisible() && opt && opt.buttons){
15758                         if(opt.buttons.ok){
15759                             handleButton("ok");
15760                         }else if(opt.buttons.yes){
15761                             handleButton("yes");
15762                         }
15763                     }
15764                 });
15765                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15766                 textareaEl.enableDisplayMode();
15767                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15768                 progressEl.enableDisplayMode();
15769                 var pf = progressEl.dom.firstChild;
15770                 if (pf) {
15771                     pp = Roo.get(pf.firstChild);
15772                     pp.setHeight(pf.offsetHeight);
15773                 }
15774                 
15775             }
15776             return dlg;
15777         },
15778
15779         /**
15780          * Updates the message box body text
15781          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15782          * the XHTML-compliant non-breaking space character '&amp;#160;')
15783          * @return {Roo.MessageBox} This message box
15784          */
15785         updateText : function(text){
15786             if(!dlg.isVisible() && !opt.width){
15787                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15788             }
15789             msgEl.innerHTML = text || '&#160;';
15790       
15791             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
15792             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
15793             var w = Math.max(
15794                     Math.min(opt.width || cw , this.maxWidth), 
15795                     Math.max(opt.minWidth || this.minWidth, bwidth)
15796             );
15797             if(opt.prompt){
15798                 activeTextEl.setWidth(w);
15799             }
15800             if(dlg.isVisible()){
15801                 dlg.fixedcenter = false;
15802             }
15803             // to big, make it scroll. = But as usual stupid IE does not support
15804             // !important..
15805             
15806             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
15807                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
15808                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
15809             } else {
15810                 bodyEl.dom.style.height = '';
15811                 bodyEl.dom.style.overflowY = '';
15812             }
15813             if (cw > w) {
15814                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
15815             } else {
15816                 bodyEl.dom.style.overflowX = '';
15817             }
15818             
15819             dlg.setContentSize(w, bodyEl.getHeight());
15820             if(dlg.isVisible()){
15821                 dlg.fixedcenter = true;
15822             }
15823             return this;
15824         },
15825
15826         /**
15827          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
15828          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15829          * @param {Number} value Any number between 0 and 1 (e.g., .5)
15830          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15831          * @return {Roo.MessageBox} This message box
15832          */
15833         updateProgress : function(value, text){
15834             if(text){
15835                 this.updateText(text);
15836             }
15837             if (pp) { // weird bug on my firefox - for some reason this is not defined
15838                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15839             }
15840             return this;
15841         },        
15842
15843         /**
15844          * Returns true if the message box is currently displayed
15845          * @return {Boolean} True if the message box is visible, else false
15846          */
15847         isVisible : function(){
15848             return dlg && dlg.isVisible();  
15849         },
15850
15851         /**
15852          * Hides the message box if it is displayed
15853          */
15854         hide : function(){
15855             if(this.isVisible()){
15856                 dlg.hide();
15857             }  
15858         },
15859
15860         /**
15861          * Displays a new message box, or reinitializes an existing message box, based on the config options
15862          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15863          * The following config object properties are supported:
15864          * <pre>
15865 Property    Type             Description
15866 ----------  ---------------  ------------------------------------------------------------------------------------
15867 animEl            String/Element   An id or Element from which the message box should animate as it opens and
15868                                    closes (defaults to undefined)
15869 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15870                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
15871 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
15872                                    progress and wait dialogs will ignore this property and always hide the
15873                                    close button as they can only be closed programmatically.
15874 cls               String           A custom CSS class to apply to the message box element
15875 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
15876                                    displayed (defaults to 75)
15877 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
15878                                    function will be btn (the name of the button that was clicked, if applicable,
15879                                    e.g. "ok"), and text (the value of the active text field, if applicable).
15880                                    Progress and wait dialogs will ignore this option since they do not respond to
15881                                    user actions and can only be closed programmatically, so any required function
15882                                    should be called by the same code after it closes the dialog.
15883 icon              String           A CSS class that provides a background image to be used as an icon for
15884                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15885 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
15886 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
15887 modal             Boolean          False to allow user interaction with the page while the message box is
15888                                    displayed (defaults to true)
15889 msg               String           A string that will replace the existing message box body text (defaults
15890                                    to the XHTML-compliant non-breaking space character '&#160;')
15891 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
15892 progress          Boolean          True to display a progress bar (defaults to false)
15893 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
15894 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
15895 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
15896 title             String           The title text
15897 value             String           The string value to set into the active textbox element if displayed
15898 wait              Boolean          True to display a progress bar (defaults to false)
15899 width             Number           The width of the dialog in pixels
15900 </pre>
15901          *
15902          * Example usage:
15903          * <pre><code>
15904 Roo.Msg.show({
15905    title: 'Address',
15906    msg: 'Please enter your address:',
15907    width: 300,
15908    buttons: Roo.MessageBox.OKCANCEL,
15909    multiline: true,
15910    fn: saveAddress,
15911    animEl: 'addAddressBtn'
15912 });
15913 </code></pre>
15914          * @param {Object} config Configuration options
15915          * @return {Roo.MessageBox} This message box
15916          */
15917         show : function(options)
15918         {
15919             
15920             // this causes nightmares if you show one dialog after another
15921             // especially on callbacks..
15922              
15923             if(this.isVisible()){
15924                 
15925                 this.hide();
15926                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
15927                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
15928                 Roo.log("New Dialog Message:" +  options.msg )
15929                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
15930                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
15931                 
15932             }
15933             var d = this.getDialog();
15934             opt = options;
15935             d.setTitle(opt.title || "&#160;");
15936             d.close.setDisplayed(opt.closable !== false);
15937             activeTextEl = textboxEl;
15938             opt.prompt = opt.prompt || (opt.multiline ? true : false);
15939             if(opt.prompt){
15940                 if(opt.multiline){
15941                     textboxEl.hide();
15942                     textareaEl.show();
15943                     textareaEl.setHeight(typeof opt.multiline == "number" ?
15944                         opt.multiline : this.defaultTextHeight);
15945                     activeTextEl = textareaEl;
15946                 }else{
15947                     textboxEl.show();
15948                     textareaEl.hide();
15949                 }
15950             }else{
15951                 textboxEl.hide();
15952                 textareaEl.hide();
15953             }
15954             progressEl.setDisplayed(opt.progress === true);
15955             this.updateProgress(0);
15956             activeTextEl.dom.value = opt.value || "";
15957             if(opt.prompt){
15958                 dlg.setDefaultButton(activeTextEl);
15959             }else{
15960                 var bs = opt.buttons;
15961                 var db = null;
15962                 if(bs && bs.ok){
15963                     db = buttons["ok"];
15964                 }else if(bs && bs.yes){
15965                     db = buttons["yes"];
15966                 }
15967                 dlg.setDefaultButton(db);
15968             }
15969             bwidth = updateButtons(opt.buttons);
15970             this.updateText(opt.msg);
15971             if(opt.cls){
15972                 d.el.addClass(opt.cls);
15973             }
15974             d.proxyDrag = opt.proxyDrag === true;
15975             d.modal = opt.modal !== false;
15976             d.mask = opt.modal !== false ? mask : false;
15977             if(!d.isVisible()){
15978                 // force it to the end of the z-index stack so it gets a cursor in FF
15979                 document.body.appendChild(dlg.el.dom);
15980                 d.animateTarget = null;
15981                 d.show(options.animEl);
15982             }
15983             return this;
15984         },
15985
15986         /**
15987          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
15988          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
15989          * and closing the message box when the process is complete.
15990          * @param {String} title The title bar text
15991          * @param {String} msg The message box body text
15992          * @return {Roo.MessageBox} This message box
15993          */
15994         progress : function(title, msg){
15995             this.show({
15996                 title : title,
15997                 msg : msg,
15998                 buttons: false,
15999                 progress:true,
16000                 closable:false,
16001                 minWidth: this.minProgressWidth,
16002                 modal : true
16003             });
16004             return this;
16005         },
16006
16007         /**
16008          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
16009          * If a callback function is passed it will be called after the user clicks the button, and the
16010          * id of the button that was clicked will be passed as the only parameter to the callback
16011          * (could also be the top-right close button).
16012          * @param {String} title The title bar text
16013          * @param {String} msg The message box body text
16014          * @param {Function} fn (optional) The callback function invoked after the message box is closed
16015          * @param {Object} scope (optional) The scope of the callback function
16016          * @return {Roo.MessageBox} This message box
16017          */
16018         alert : function(title, msg, fn, scope){
16019             this.show({
16020                 title : title,
16021                 msg : msg,
16022                 buttons: this.OK,
16023                 fn: fn,
16024                 scope : scope,
16025                 modal : true
16026             });
16027             return this;
16028         },
16029
16030         /**
16031          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
16032          * interaction while waiting for a long-running process to complete that does not have defined intervals.
16033          * You are responsible for closing the message box when the process is complete.
16034          * @param {String} msg The message box body text
16035          * @param {String} title (optional) The title bar text
16036          * @return {Roo.MessageBox} This message box
16037          */
16038         wait : function(msg, title){
16039             this.show({
16040                 title : title,
16041                 msg : msg,
16042                 buttons: false,
16043                 closable:false,
16044                 progress:true,
16045                 modal:true,
16046                 width:300,
16047                 wait:true
16048             });
16049             waitTimer = Roo.TaskMgr.start({
16050                 run: function(i){
16051                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
16052                 },
16053                 interval: 1000
16054             });
16055             return this;
16056         },
16057
16058         /**
16059          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
16060          * If a callback function is passed it will be called after the user clicks either button, and the id of the
16061          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
16062          * @param {String} title The title bar text
16063          * @param {String} msg The message box body text
16064          * @param {Function} fn (optional) The callback function invoked after the message box is closed
16065          * @param {Object} scope (optional) The scope of the callback function
16066          * @return {Roo.MessageBox} This message box
16067          */
16068         confirm : function(title, msg, fn, scope){
16069             this.show({
16070                 title : title,
16071                 msg : msg,
16072                 buttons: this.YESNO,
16073                 fn: fn,
16074                 scope : scope,
16075                 modal : true
16076             });
16077             return this;
16078         },
16079
16080         /**
16081          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
16082          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
16083          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
16084          * (could also be the top-right close button) and the text that was entered will be passed as the two
16085          * parameters to the callback.
16086          * @param {String} title The title bar text
16087          * @param {String} msg The message box body text
16088          * @param {Function} fn (optional) The callback function invoked after the message box is closed
16089          * @param {Object} scope (optional) The scope of the callback function
16090          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
16091          * property, or the height in pixels to create the textbox (defaults to false / single-line)
16092          * @return {Roo.MessageBox} This message box
16093          */
16094         prompt : function(title, msg, fn, scope, multiline){
16095             this.show({
16096                 title : title,
16097                 msg : msg,
16098                 buttons: this.OKCANCEL,
16099                 fn: fn,
16100                 minWidth:250,
16101                 scope : scope,
16102                 prompt:true,
16103                 multiline: multiline,
16104                 modal : true
16105             });
16106             return this;
16107         },
16108
16109         /**
16110          * Button config that displays a single OK button
16111          * @type Object
16112          */
16113         OK : {ok:true},
16114         /**
16115          * Button config that displays Yes and No buttons
16116          * @type Object
16117          */
16118         YESNO : {yes:true, no:true},
16119         /**
16120          * Button config that displays OK and Cancel buttons
16121          * @type Object
16122          */
16123         OKCANCEL : {ok:true, cancel:true},
16124         /**
16125          * Button config that displays Yes, No and Cancel buttons
16126          * @type Object
16127          */
16128         YESNOCANCEL : {yes:true, no:true, cancel:true},
16129
16130         /**
16131          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
16132          * @type Number
16133          */
16134         defaultTextHeight : 75,
16135         /**
16136          * The maximum width in pixels of the message box (defaults to 600)
16137          * @type Number
16138          */
16139         maxWidth : 600,
16140         /**
16141          * The minimum width in pixels of the message box (defaults to 100)
16142          * @type Number
16143          */
16144         minWidth : 100,
16145         /**
16146          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
16147          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
16148          * @type Number
16149          */
16150         minProgressWidth : 250,
16151         /**
16152          * An object containing the default button text strings that can be overriden for localized language support.
16153          * Supported properties are: ok, cancel, yes and no.
16154          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
16155          * @type Object
16156          */
16157         buttonText : {
16158             ok : "OK",
16159             cancel : "Cancel",
16160             yes : "Yes",
16161             no : "No"
16162         }
16163     };
16164 }();
16165
16166 /**
16167  * Shorthand for {@link Roo.MessageBox}
16168  */
16169 Roo.Msg = Roo.MessageBox;/*
16170  * Based on:
16171  * Ext JS Library 1.1.1
16172  * Copyright(c) 2006-2007, Ext JS, LLC.
16173  *
16174  * Originally Released Under LGPL - original licence link has changed is not relivant.
16175  *
16176  * Fork - LGPL
16177  * <script type="text/javascript">
16178  */
16179 /**
16180  * @class Roo.QuickTips
16181  * Provides attractive and customizable tooltips for any element.
16182  * @singleton
16183  */
16184 Roo.QuickTips = function(){
16185     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
16186     var ce, bd, xy, dd;
16187     var visible = false, disabled = true, inited = false;
16188     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
16189     
16190     var onOver = function(e){
16191         if(disabled){
16192             return;
16193         }
16194         var t = e.getTarget();
16195         if(!t || t.nodeType !== 1 || t == document || t == document.body){
16196             return;
16197         }
16198         if(ce && t == ce.el){
16199             clearTimeout(hideProc);
16200             return;
16201         }
16202         if(t && tagEls[t.id]){
16203             tagEls[t.id].el = t;
16204             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
16205             return;
16206         }
16207         var ttp, et = Roo.fly(t);
16208         var ns = cfg.namespace;
16209         if(tm.interceptTitles && t.title){
16210             ttp = t.title;
16211             t.qtip = ttp;
16212             t.removeAttribute("title");
16213             e.preventDefault();
16214         }else{
16215             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
16216         }
16217         if(ttp){
16218             showProc = show.defer(tm.showDelay, tm, [{
16219                 el: t, 
16220                 text: ttp, 
16221                 width: et.getAttributeNS(ns, cfg.width),
16222                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
16223                 title: et.getAttributeNS(ns, cfg.title),
16224                     cls: et.getAttributeNS(ns, cfg.cls)
16225             }]);
16226         }
16227     };
16228     
16229     var onOut = function(e){
16230         clearTimeout(showProc);
16231         var t = e.getTarget();
16232         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
16233             hideProc = setTimeout(hide, tm.hideDelay);
16234         }
16235     };
16236     
16237     var onMove = function(e){
16238         if(disabled){
16239             return;
16240         }
16241         xy = e.getXY();
16242         xy[1] += 18;
16243         if(tm.trackMouse && ce){
16244             el.setXY(xy);
16245         }
16246     };
16247     
16248     var onDown = function(e){
16249         clearTimeout(showProc);
16250         clearTimeout(hideProc);
16251         if(!e.within(el)){
16252             if(tm.hideOnClick){
16253                 hide();
16254                 tm.disable();
16255                 tm.enable.defer(100, tm);
16256             }
16257         }
16258     };
16259     
16260     var getPad = function(){
16261         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
16262     };
16263
16264     var show = function(o){
16265         if(disabled){
16266             return;
16267         }
16268         clearTimeout(dismissProc);
16269         ce = o;
16270         if(removeCls){ // in case manually hidden
16271             el.removeClass(removeCls);
16272             removeCls = null;
16273         }
16274         if(ce.cls){
16275             el.addClass(ce.cls);
16276             removeCls = ce.cls;
16277         }
16278         if(ce.title){
16279             tipTitle.update(ce.title);
16280             tipTitle.show();
16281         }else{
16282             tipTitle.update('');
16283             tipTitle.hide();
16284         }
16285         el.dom.style.width  = tm.maxWidth+'px';
16286         //tipBody.dom.style.width = '';
16287         tipBodyText.update(o.text);
16288         var p = getPad(), w = ce.width;
16289         if(!w){
16290             var td = tipBodyText.dom;
16291             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
16292             if(aw > tm.maxWidth){
16293                 w = tm.maxWidth;
16294             }else if(aw < tm.minWidth){
16295                 w = tm.minWidth;
16296             }else{
16297                 w = aw;
16298             }
16299         }
16300         //tipBody.setWidth(w);
16301         el.setWidth(parseInt(w, 10) + p);
16302         if(ce.autoHide === false){
16303             close.setDisplayed(true);
16304             if(dd){
16305                 dd.unlock();
16306             }
16307         }else{
16308             close.setDisplayed(false);
16309             if(dd){
16310                 dd.lock();
16311             }
16312         }
16313         if(xy){
16314             el.avoidY = xy[1]-18;
16315             el.setXY(xy);
16316         }
16317         if(tm.animate){
16318             el.setOpacity(.1);
16319             el.setStyle("visibility", "visible");
16320             el.fadeIn({callback: afterShow});
16321         }else{
16322             afterShow();
16323         }
16324     };
16325     
16326     var afterShow = function(){
16327         if(ce){
16328             el.show();
16329             esc.enable();
16330             if(tm.autoDismiss && ce.autoHide !== false){
16331                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
16332             }
16333         }
16334     };
16335     
16336     var hide = function(noanim){
16337         clearTimeout(dismissProc);
16338         clearTimeout(hideProc);
16339         ce = null;
16340         if(el.isVisible()){
16341             esc.disable();
16342             if(noanim !== true && tm.animate){
16343                 el.fadeOut({callback: afterHide});
16344             }else{
16345                 afterHide();
16346             } 
16347         }
16348     };
16349     
16350     var afterHide = function(){
16351         el.hide();
16352         if(removeCls){
16353             el.removeClass(removeCls);
16354             removeCls = null;
16355         }
16356     };
16357     
16358     return {
16359         /**
16360         * @cfg {Number} minWidth
16361         * The minimum width of the quick tip (defaults to 40)
16362         */
16363        minWidth : 40,
16364         /**
16365         * @cfg {Number} maxWidth
16366         * The maximum width of the quick tip (defaults to 300)
16367         */
16368        maxWidth : 300,
16369         /**
16370         * @cfg {Boolean} interceptTitles
16371         * True to automatically use the element's DOM title value if available (defaults to false)
16372         */
16373        interceptTitles : false,
16374         /**
16375         * @cfg {Boolean} trackMouse
16376         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
16377         */
16378        trackMouse : false,
16379         /**
16380         * @cfg {Boolean} hideOnClick
16381         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
16382         */
16383        hideOnClick : true,
16384         /**
16385         * @cfg {Number} showDelay
16386         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
16387         */
16388        showDelay : 500,
16389         /**
16390         * @cfg {Number} hideDelay
16391         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
16392         */
16393        hideDelay : 200,
16394         /**
16395         * @cfg {Boolean} autoHide
16396         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
16397         * Used in conjunction with hideDelay.
16398         */
16399        autoHide : true,
16400         /**
16401         * @cfg {Boolean}
16402         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
16403         * (defaults to true).  Used in conjunction with autoDismissDelay.
16404         */
16405        autoDismiss : true,
16406         /**
16407         * @cfg {Number}
16408         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
16409         */
16410        autoDismissDelay : 5000,
16411        /**
16412         * @cfg {Boolean} animate
16413         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
16414         */
16415        animate : false,
16416
16417        /**
16418         * @cfg {String} title
16419         * Title text to display (defaults to '').  This can be any valid HTML markup.
16420         */
16421         title: '',
16422        /**
16423         * @cfg {String} text
16424         * Body text to display (defaults to '').  This can be any valid HTML markup.
16425         */
16426         text : '',
16427        /**
16428         * @cfg {String} cls
16429         * A CSS class to apply to the base quick tip element (defaults to '').
16430         */
16431         cls : '',
16432        /**
16433         * @cfg {Number} width
16434         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
16435         * minWidth or maxWidth.
16436         */
16437         width : null,
16438
16439     /**
16440      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
16441      * or display QuickTips in a page.
16442      */
16443        init : function(){
16444           tm = Roo.QuickTips;
16445           cfg = tm.tagConfig;
16446           if(!inited){
16447               if(!Roo.isReady){ // allow calling of init() before onReady
16448                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
16449                   return;
16450               }
16451               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
16452               el.fxDefaults = {stopFx: true};
16453               // maximum custom styling
16454               //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>');
16455               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>');              
16456               tipTitle = el.child('h3');
16457               tipTitle.enableDisplayMode("block");
16458               tipBody = el.child('div.x-tip-bd');
16459               tipBodyText = el.child('div.x-tip-bd-inner');
16460               //bdLeft = el.child('div.x-tip-bd-left');
16461               //bdRight = el.child('div.x-tip-bd-right');
16462               close = el.child('div.x-tip-close');
16463               close.enableDisplayMode("block");
16464               close.on("click", hide);
16465               var d = Roo.get(document);
16466               d.on("mousedown", onDown);
16467               d.on("mouseover", onOver);
16468               d.on("mouseout", onOut);
16469               d.on("mousemove", onMove);
16470               esc = d.addKeyListener(27, hide);
16471               esc.disable();
16472               if(Roo.dd.DD){
16473                   dd = el.initDD("default", null, {
16474                       onDrag : function(){
16475                           el.sync();  
16476                       }
16477                   });
16478                   dd.setHandleElId(tipTitle.id);
16479                   dd.lock();
16480               }
16481               inited = true;
16482           }
16483           this.enable(); 
16484        },
16485
16486     /**
16487      * Configures a new quick tip instance and assigns it to a target element.  The following config options
16488      * are supported:
16489      * <pre>
16490 Property    Type                   Description
16491 ----------  ---------------------  ------------------------------------------------------------------------
16492 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
16493      * </ul>
16494      * @param {Object} config The config object
16495      */
16496        register : function(config){
16497            var cs = config instanceof Array ? config : arguments;
16498            for(var i = 0, len = cs.length; i < len; i++) {
16499                var c = cs[i];
16500                var target = c.target;
16501                if(target){
16502                    if(target instanceof Array){
16503                        for(var j = 0, jlen = target.length; j < jlen; j++){
16504                            tagEls[target[j]] = c;
16505                        }
16506                    }else{
16507                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
16508                    }
16509                }
16510            }
16511        },
16512
16513     /**
16514      * Removes this quick tip from its element and destroys it.
16515      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
16516      */
16517        unregister : function(el){
16518            delete tagEls[Roo.id(el)];
16519        },
16520
16521     /**
16522      * Enable this quick tip.
16523      */
16524        enable : function(){
16525            if(inited && disabled){
16526                locks.pop();
16527                if(locks.length < 1){
16528                    disabled = false;
16529                }
16530            }
16531        },
16532
16533     /**
16534      * Disable this quick tip.
16535      */
16536        disable : function(){
16537           disabled = true;
16538           clearTimeout(showProc);
16539           clearTimeout(hideProc);
16540           clearTimeout(dismissProc);
16541           if(ce){
16542               hide(true);
16543           }
16544           locks.push(1);
16545        },
16546
16547     /**
16548      * Returns true if the quick tip is enabled, else false.
16549      */
16550        isEnabled : function(){
16551             return !disabled;
16552        },
16553
16554         // private
16555        tagConfig : {
16556            namespace : "ext",
16557            attribute : "qtip",
16558            width : "width",
16559            target : "target",
16560            title : "qtitle",
16561            hide : "hide",
16562            cls : "qclass"
16563        }
16564    };
16565 }();
16566
16567 // backwards compat
16568 Roo.QuickTips.tips = Roo.QuickTips.register;/*
16569  * Based on:
16570  * Ext JS Library 1.1.1
16571  * Copyright(c) 2006-2007, Ext JS, LLC.
16572  *
16573  * Originally Released Under LGPL - original licence link has changed is not relivant.
16574  *
16575  * Fork - LGPL
16576  * <script type="text/javascript">
16577  */
16578  
16579
16580 /**
16581  * @class Roo.tree.TreePanel
16582  * @extends Roo.data.Tree
16583
16584  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
16585  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
16586  * @cfg {Boolean} enableDD true to enable drag and drop
16587  * @cfg {Boolean} enableDrag true to enable just drag
16588  * @cfg {Boolean} enableDrop true to enable just drop
16589  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
16590  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
16591  * @cfg {String} ddGroup The DD group this TreePanel belongs to
16592  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
16593  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
16594  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
16595  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
16596  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
16597  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
16598  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
16599  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
16600  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
16601  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
16602  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
16603  * @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>
16604  * @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>
16605  * 
16606  * @constructor
16607  * @param {String/HTMLElement/Element} el The container element
16608  * @param {Object} config
16609  */
16610 Roo.tree.TreePanel = function(el, config){
16611     var root = false;
16612     var loader = false;
16613     if (config.root) {
16614         root = config.root;
16615         delete config.root;
16616     }
16617     if (config.loader) {
16618         loader = config.loader;
16619         delete config.loader;
16620     }
16621     
16622     Roo.apply(this, config);
16623     Roo.tree.TreePanel.superclass.constructor.call(this);
16624     this.el = Roo.get(el);
16625     this.el.addClass('x-tree');
16626     //console.log(root);
16627     if (root) {
16628         this.setRootNode( Roo.factory(root, Roo.tree));
16629     }
16630     if (loader) {
16631         this.loader = Roo.factory(loader, Roo.tree);
16632     }
16633    /**
16634     * Read-only. The id of the container element becomes this TreePanel's id.
16635     */
16636     this.id = this.el.id;
16637     this.addEvents({
16638         /**
16639         * @event beforeload
16640         * Fires before a node is loaded, return false to cancel
16641         * @param {Node} node The node being loaded
16642         */
16643         "beforeload" : true,
16644         /**
16645         * @event load
16646         * Fires when a node is loaded
16647         * @param {Node} node The node that was loaded
16648         */
16649         "load" : true,
16650         /**
16651         * @event textchange
16652         * Fires when the text for a node is changed
16653         * @param {Node} node The node
16654         * @param {String} text The new text
16655         * @param {String} oldText The old text
16656         */
16657         "textchange" : true,
16658         /**
16659         * @event beforeexpand
16660         * Fires before a node is expanded, return false to cancel.
16661         * @param {Node} node The node
16662         * @param {Boolean} deep
16663         * @param {Boolean} anim
16664         */
16665         "beforeexpand" : true,
16666         /**
16667         * @event beforecollapse
16668         * Fires before a node is collapsed, return false to cancel.
16669         * @param {Node} node The node
16670         * @param {Boolean} deep
16671         * @param {Boolean} anim
16672         */
16673         "beforecollapse" : true,
16674         /**
16675         * @event expand
16676         * Fires when a node is expanded
16677         * @param {Node} node The node
16678         */
16679         "expand" : true,
16680         /**
16681         * @event disabledchange
16682         * Fires when the disabled status of a node changes
16683         * @param {Node} node The node
16684         * @param {Boolean} disabled
16685         */
16686         "disabledchange" : true,
16687         /**
16688         * @event collapse
16689         * Fires when a node is collapsed
16690         * @param {Node} node The node
16691         */
16692         "collapse" : true,
16693         /**
16694         * @event beforeclick
16695         * Fires before click processing on a node. Return false to cancel the default action.
16696         * @param {Node} node The node
16697         * @param {Roo.EventObject} e The event object
16698         */
16699         "beforeclick":true,
16700         /**
16701         * @event checkchange
16702         * Fires when a node with a checkbox's checked property changes
16703         * @param {Node} this This node
16704         * @param {Boolean} checked
16705         */
16706         "checkchange":true,
16707         /**
16708         * @event click
16709         * Fires when a node is clicked
16710         * @param {Node} node The node
16711         * @param {Roo.EventObject} e The event object
16712         */
16713         "click":true,
16714         /**
16715         * @event dblclick
16716         * Fires when a node is double clicked
16717         * @param {Node} node The node
16718         * @param {Roo.EventObject} e The event object
16719         */
16720         "dblclick":true,
16721         /**
16722         * @event contextmenu
16723         * Fires when a node is right clicked
16724         * @param {Node} node The node
16725         * @param {Roo.EventObject} e The event object
16726         */
16727         "contextmenu":true,
16728         /**
16729         * @event beforechildrenrendered
16730         * Fires right before the child nodes for a node are rendered
16731         * @param {Node} node The node
16732         */
16733         "beforechildrenrendered":true,
16734         /**
16735         * @event startdrag
16736         * Fires when a node starts being dragged
16737         * @param {Roo.tree.TreePanel} this
16738         * @param {Roo.tree.TreeNode} node
16739         * @param {event} e The raw browser event
16740         */ 
16741        "startdrag" : true,
16742        /**
16743         * @event enddrag
16744         * Fires when a drag operation is complete
16745         * @param {Roo.tree.TreePanel} this
16746         * @param {Roo.tree.TreeNode} node
16747         * @param {event} e The raw browser event
16748         */
16749        "enddrag" : true,
16750        /**
16751         * @event dragdrop
16752         * Fires when a dragged node is dropped on a valid DD target
16753         * @param {Roo.tree.TreePanel} this
16754         * @param {Roo.tree.TreeNode} node
16755         * @param {DD} dd The dd it was dropped on
16756         * @param {event} e The raw browser event
16757         */
16758        "dragdrop" : true,
16759        /**
16760         * @event beforenodedrop
16761         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16762         * passed to handlers has the following properties:<br />
16763         * <ul style="padding:5px;padding-left:16px;">
16764         * <li>tree - The TreePanel</li>
16765         * <li>target - The node being targeted for the drop</li>
16766         * <li>data - The drag data from the drag source</li>
16767         * <li>point - The point of the drop - append, above or below</li>
16768         * <li>source - The drag source</li>
16769         * <li>rawEvent - Raw mouse event</li>
16770         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16771         * to be inserted by setting them on this object.</li>
16772         * <li>cancel - Set this to true to cancel the drop.</li>
16773         * </ul>
16774         * @param {Object} dropEvent
16775         */
16776        "beforenodedrop" : true,
16777        /**
16778         * @event nodedrop
16779         * Fires after a DD object is dropped on a node in this tree. The dropEvent
16780         * passed to handlers has the following properties:<br />
16781         * <ul style="padding:5px;padding-left:16px;">
16782         * <li>tree - The TreePanel</li>
16783         * <li>target - The node being targeted for the drop</li>
16784         * <li>data - The drag data from the drag source</li>
16785         * <li>point - The point of the drop - append, above or below</li>
16786         * <li>source - The drag source</li>
16787         * <li>rawEvent - Raw mouse event</li>
16788         * <li>dropNode - Dropped node(s).</li>
16789         * </ul>
16790         * @param {Object} dropEvent
16791         */
16792        "nodedrop" : true,
16793         /**
16794         * @event nodedragover
16795         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16796         * passed to handlers has the following properties:<br />
16797         * <ul style="padding:5px;padding-left:16px;">
16798         * <li>tree - The TreePanel</li>
16799         * <li>target - The node being targeted for the drop</li>
16800         * <li>data - The drag data from the drag source</li>
16801         * <li>point - The point of the drop - append, above or below</li>
16802         * <li>source - The drag source</li>
16803         * <li>rawEvent - Raw mouse event</li>
16804         * <li>dropNode - Drop node(s) provided by the source.</li>
16805         * <li>cancel - Set this to true to signal drop not allowed.</li>
16806         * </ul>
16807         * @param {Object} dragOverEvent
16808         */
16809        "nodedragover" : true
16810         
16811     });
16812     if(this.singleExpand){
16813        this.on("beforeexpand", this.restrictExpand, this);
16814     }
16815     if (this.editor) {
16816         this.editor.tree = this;
16817         this.editor = Roo.factory(this.editor, Roo.tree);
16818     }
16819     
16820     if (this.selModel) {
16821         this.selModel = Roo.factory(this.selModel, Roo.tree);
16822     }
16823    
16824 };
16825 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16826     rootVisible : true,
16827     animate: Roo.enableFx,
16828     lines : true,
16829     enableDD : false,
16830     hlDrop : Roo.enableFx,
16831   
16832     renderer: false,
16833     
16834     rendererTip: false,
16835     // private
16836     restrictExpand : function(node){
16837         var p = node.parentNode;
16838         if(p){
16839             if(p.expandedChild && p.expandedChild.parentNode == p){
16840                 p.expandedChild.collapse();
16841             }
16842             p.expandedChild = node;
16843         }
16844     },
16845
16846     // private override
16847     setRootNode : function(node){
16848         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16849         if(!this.rootVisible){
16850             node.ui = new Roo.tree.RootTreeNodeUI(node);
16851         }
16852         return node;
16853     },
16854
16855     /**
16856      * Returns the container element for this TreePanel
16857      */
16858     getEl : function(){
16859         return this.el;
16860     },
16861
16862     /**
16863      * Returns the default TreeLoader for this TreePanel
16864      */
16865     getLoader : function(){
16866         return this.loader;
16867     },
16868
16869     /**
16870      * Expand all nodes
16871      */
16872     expandAll : function(){
16873         this.root.expand(true);
16874     },
16875
16876     /**
16877      * Collapse all nodes
16878      */
16879     collapseAll : function(){
16880         this.root.collapse(true);
16881     },
16882
16883     /**
16884      * Returns the selection model used by this TreePanel
16885      */
16886     getSelectionModel : function(){
16887         if(!this.selModel){
16888             this.selModel = new Roo.tree.DefaultSelectionModel();
16889         }
16890         return this.selModel;
16891     },
16892
16893     /**
16894      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16895      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16896      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16897      * @return {Array}
16898      */
16899     getChecked : function(a, startNode){
16900         startNode = startNode || this.root;
16901         var r = [];
16902         var f = function(){
16903             if(this.attributes.checked){
16904                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16905             }
16906         }
16907         startNode.cascade(f);
16908         return r;
16909     },
16910
16911     /**
16912      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16913      * @param {String} path
16914      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16915      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16916      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16917      */
16918     expandPath : function(path, attr, callback){
16919         attr = attr || "id";
16920         var keys = path.split(this.pathSeparator);
16921         var curNode = this.root;
16922         if(curNode.attributes[attr] != keys[1]){ // invalid root
16923             if(callback){
16924                 callback(false, null);
16925             }
16926             return;
16927         }
16928         var index = 1;
16929         var f = function(){
16930             if(++index == keys.length){
16931                 if(callback){
16932                     callback(true, curNode);
16933                 }
16934                 return;
16935             }
16936             var c = curNode.findChild(attr, keys[index]);
16937             if(!c){
16938                 if(callback){
16939                     callback(false, curNode);
16940                 }
16941                 return;
16942             }
16943             curNode = c;
16944             c.expand(false, false, f);
16945         };
16946         curNode.expand(false, false, f);
16947     },
16948
16949     /**
16950      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16951      * @param {String} path
16952      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16953      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16954      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16955      */
16956     selectPath : function(path, attr, callback){
16957         attr = attr || "id";
16958         var keys = path.split(this.pathSeparator);
16959         var v = keys.pop();
16960         if(keys.length > 0){
16961             var f = function(success, node){
16962                 if(success && node){
16963                     var n = node.findChild(attr, v);
16964                     if(n){
16965                         n.select();
16966                         if(callback){
16967                             callback(true, n);
16968                         }
16969                     }else if(callback){
16970                         callback(false, n);
16971                     }
16972                 }else{
16973                     if(callback){
16974                         callback(false, n);
16975                     }
16976                 }
16977             };
16978             this.expandPath(keys.join(this.pathSeparator), attr, f);
16979         }else{
16980             this.root.select();
16981             if(callback){
16982                 callback(true, this.root);
16983             }
16984         }
16985     },
16986
16987     getTreeEl : function(){
16988         return this.el;
16989     },
16990
16991     /**
16992      * Trigger rendering of this TreePanel
16993      */
16994     render : function(){
16995         if (this.innerCt) {
16996             return this; // stop it rendering more than once!!
16997         }
16998         
16999         this.innerCt = this.el.createChild({tag:"ul",
17000                cls:"x-tree-root-ct " +
17001                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
17002
17003         if(this.containerScroll){
17004             Roo.dd.ScrollManager.register(this.el);
17005         }
17006         if((this.enableDD || this.enableDrop) && !this.dropZone){
17007            /**
17008             * The dropZone used by this tree if drop is enabled
17009             * @type Roo.tree.TreeDropZone
17010             */
17011              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
17012                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
17013            });
17014         }
17015         if((this.enableDD || this.enableDrag) && !this.dragZone){
17016            /**
17017             * The dragZone used by this tree if drag is enabled
17018             * @type Roo.tree.TreeDragZone
17019             */
17020             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
17021                ddGroup: this.ddGroup || "TreeDD",
17022                scroll: this.ddScroll
17023            });
17024         }
17025         this.getSelectionModel().init(this);
17026         if (!this.root) {
17027             Roo.log("ROOT not set in tree");
17028             return this;
17029         }
17030         this.root.render();
17031         if(!this.rootVisible){
17032             this.root.renderChildren();
17033         }
17034         return this;
17035     }
17036 });/*
17037  * Based on:
17038  * Ext JS Library 1.1.1
17039  * Copyright(c) 2006-2007, Ext JS, LLC.
17040  *
17041  * Originally Released Under LGPL - original licence link has changed is not relivant.
17042  *
17043  * Fork - LGPL
17044  * <script type="text/javascript">
17045  */
17046  
17047
17048 /**
17049  * @class Roo.tree.DefaultSelectionModel
17050  * @extends Roo.util.Observable
17051  * The default single selection for a TreePanel.
17052  * @param {Object} cfg Configuration
17053  */
17054 Roo.tree.DefaultSelectionModel = function(cfg){
17055    this.selNode = null;
17056    
17057    
17058    
17059    this.addEvents({
17060        /**
17061         * @event selectionchange
17062         * Fires when the selected node changes
17063         * @param {DefaultSelectionModel} this
17064         * @param {TreeNode} node the new selection
17065         */
17066        "selectionchange" : true,
17067
17068        /**
17069         * @event beforeselect
17070         * Fires before the selected node changes, return false to cancel the change
17071         * @param {DefaultSelectionModel} this
17072         * @param {TreeNode} node the new selection
17073         * @param {TreeNode} node the old selection
17074         */
17075        "beforeselect" : true
17076    });
17077    
17078     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
17079 };
17080
17081 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
17082     init : function(tree){
17083         this.tree = tree;
17084         tree.getTreeEl().on("keydown", this.onKeyDown, this);
17085         tree.on("click", this.onNodeClick, this);
17086     },
17087     
17088     onNodeClick : function(node, e){
17089         if (e.ctrlKey && this.selNode == node)  {
17090             this.unselect(node);
17091             return;
17092         }
17093         this.select(node);
17094     },
17095     
17096     /**
17097      * Select a node.
17098      * @param {TreeNode} node The node to select
17099      * @return {TreeNode} The selected node
17100      */
17101     select : function(node){
17102         var last = this.selNode;
17103         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
17104             if(last){
17105                 last.ui.onSelectedChange(false);
17106             }
17107             this.selNode = node;
17108             node.ui.onSelectedChange(true);
17109             this.fireEvent("selectionchange", this, node, last);
17110         }
17111         return node;
17112     },
17113     
17114     /**
17115      * Deselect a node.
17116      * @param {TreeNode} node The node to unselect
17117      */
17118     unselect : function(node){
17119         if(this.selNode == node){
17120             this.clearSelections();
17121         }    
17122     },
17123     
17124     /**
17125      * Clear all selections
17126      */
17127     clearSelections : function(){
17128         var n = this.selNode;
17129         if(n){
17130             n.ui.onSelectedChange(false);
17131             this.selNode = null;
17132             this.fireEvent("selectionchange", this, null);
17133         }
17134         return n;
17135     },
17136     
17137     /**
17138      * Get the selected node
17139      * @return {TreeNode} The selected node
17140      */
17141     getSelectedNode : function(){
17142         return this.selNode;    
17143     },
17144     
17145     /**
17146      * Returns true if the node is selected
17147      * @param {TreeNode} node The node to check
17148      * @return {Boolean}
17149      */
17150     isSelected : function(node){
17151         return this.selNode == node;  
17152     },
17153
17154     /**
17155      * Selects the node above the selected node in the tree, intelligently walking the nodes
17156      * @return TreeNode The new selection
17157      */
17158     selectPrevious : function(){
17159         var s = this.selNode || this.lastSelNode;
17160         if(!s){
17161             return null;
17162         }
17163         var ps = s.previousSibling;
17164         if(ps){
17165             if(!ps.isExpanded() || ps.childNodes.length < 1){
17166                 return this.select(ps);
17167             } else{
17168                 var lc = ps.lastChild;
17169                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
17170                     lc = lc.lastChild;
17171                 }
17172                 return this.select(lc);
17173             }
17174         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
17175             return this.select(s.parentNode);
17176         }
17177         return null;
17178     },
17179
17180     /**
17181      * Selects the node above the selected node in the tree, intelligently walking the nodes
17182      * @return TreeNode The new selection
17183      */
17184     selectNext : function(){
17185         var s = this.selNode || this.lastSelNode;
17186         if(!s){
17187             return null;
17188         }
17189         if(s.firstChild && s.isExpanded()){
17190              return this.select(s.firstChild);
17191          }else if(s.nextSibling){
17192              return this.select(s.nextSibling);
17193          }else if(s.parentNode){
17194             var newS = null;
17195             s.parentNode.bubble(function(){
17196                 if(this.nextSibling){
17197                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
17198                     return false;
17199                 }
17200             });
17201             return newS;
17202          }
17203         return null;
17204     },
17205
17206     onKeyDown : function(e){
17207         var s = this.selNode || this.lastSelNode;
17208         // undesirable, but required
17209         var sm = this;
17210         if(!s){
17211             return;
17212         }
17213         var k = e.getKey();
17214         switch(k){
17215              case e.DOWN:
17216                  e.stopEvent();
17217                  this.selectNext();
17218              break;
17219              case e.UP:
17220                  e.stopEvent();
17221                  this.selectPrevious();
17222              break;
17223              case e.RIGHT:
17224                  e.preventDefault();
17225                  if(s.hasChildNodes()){
17226                      if(!s.isExpanded()){
17227                          s.expand();
17228                      }else if(s.firstChild){
17229                          this.select(s.firstChild, e);
17230                      }
17231                  }
17232              break;
17233              case e.LEFT:
17234                  e.preventDefault();
17235                  if(s.hasChildNodes() && s.isExpanded()){
17236                      s.collapse();
17237                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
17238                      this.select(s.parentNode, e);
17239                  }
17240              break;
17241         };
17242     }
17243 });
17244
17245 /**
17246  * @class Roo.tree.MultiSelectionModel
17247  * @extends Roo.util.Observable
17248  * Multi selection for a TreePanel.
17249  * @param {Object} cfg Configuration
17250  */
17251 Roo.tree.MultiSelectionModel = function(){
17252    this.selNodes = [];
17253    this.selMap = {};
17254    this.addEvents({
17255        /**
17256         * @event selectionchange
17257         * Fires when the selected nodes change
17258         * @param {MultiSelectionModel} this
17259         * @param {Array} nodes Array of the selected nodes
17260         */
17261        "selectionchange" : true
17262    });
17263    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
17264    
17265 };
17266
17267 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
17268     init : function(tree){
17269         this.tree = tree;
17270         tree.getTreeEl().on("keydown", this.onKeyDown, this);
17271         tree.on("click", this.onNodeClick, this);
17272     },
17273     
17274     onNodeClick : function(node, e){
17275         this.select(node, e, e.ctrlKey);
17276     },
17277     
17278     /**
17279      * Select a node.
17280      * @param {TreeNode} node The node to select
17281      * @param {EventObject} e (optional) An event associated with the selection
17282      * @param {Boolean} keepExisting True to retain existing selections
17283      * @return {TreeNode} The selected node
17284      */
17285     select : function(node, e, keepExisting){
17286         if(keepExisting !== true){
17287             this.clearSelections(true);
17288         }
17289         if(this.isSelected(node)){
17290             this.lastSelNode = node;
17291             return node;
17292         }
17293         this.selNodes.push(node);
17294         this.selMap[node.id] = node;
17295         this.lastSelNode = node;
17296         node.ui.onSelectedChange(true);
17297         this.fireEvent("selectionchange", this, this.selNodes);
17298         return node;
17299     },
17300     
17301     /**
17302      * Deselect a node.
17303      * @param {TreeNode} node The node to unselect
17304      */
17305     unselect : function(node){
17306         if(this.selMap[node.id]){
17307             node.ui.onSelectedChange(false);
17308             var sn = this.selNodes;
17309             var index = -1;
17310             if(sn.indexOf){
17311                 index = sn.indexOf(node);
17312             }else{
17313                 for(var i = 0, len = sn.length; i < len; i++){
17314                     if(sn[i] == node){
17315                         index = i;
17316                         break;
17317                     }
17318                 }
17319             }
17320             if(index != -1){
17321                 this.selNodes.splice(index, 1);
17322             }
17323             delete this.selMap[node.id];
17324             this.fireEvent("selectionchange", this, this.selNodes);
17325         }
17326     },
17327     
17328     /**
17329      * Clear all selections
17330      */
17331     clearSelections : function(suppressEvent){
17332         var sn = this.selNodes;
17333         if(sn.length > 0){
17334             for(var i = 0, len = sn.length; i < len; i++){
17335                 sn[i].ui.onSelectedChange(false);
17336             }
17337             this.selNodes = [];
17338             this.selMap = {};
17339             if(suppressEvent !== true){
17340                 this.fireEvent("selectionchange", this, this.selNodes);
17341             }
17342         }
17343     },
17344     
17345     /**
17346      * Returns true if the node is selected
17347      * @param {TreeNode} node The node to check
17348      * @return {Boolean}
17349      */
17350     isSelected : function(node){
17351         return this.selMap[node.id] ? true : false;  
17352     },
17353     
17354     /**
17355      * Returns an array of the selected nodes
17356      * @return {Array}
17357      */
17358     getSelectedNodes : function(){
17359         return this.selNodes;    
17360     },
17361
17362     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
17363
17364     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
17365
17366     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
17367 });/*
17368  * Based on:
17369  * Ext JS Library 1.1.1
17370  * Copyright(c) 2006-2007, Ext JS, LLC.
17371  *
17372  * Originally Released Under LGPL - original licence link has changed is not relivant.
17373  *
17374  * Fork - LGPL
17375  * <script type="text/javascript">
17376  */
17377  
17378 /**
17379  * @class Roo.tree.TreeNode
17380  * @extends Roo.data.Node
17381  * @cfg {String} text The text for this node
17382  * @cfg {Boolean} expanded true to start the node expanded
17383  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
17384  * @cfg {Boolean} allowDrop false if this node cannot be drop on
17385  * @cfg {Boolean} disabled true to start the node disabled
17386  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
17387  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
17388  * @cfg {String} cls A css class to be added to the node
17389  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
17390  * @cfg {String} href URL of the link used for the node (defaults to #)
17391  * @cfg {String} hrefTarget target frame for the link
17392  * @cfg {String} qtip An Ext QuickTip for the node
17393  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
17394  * @cfg {Boolean} singleClickExpand True for single click expand on this node
17395  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
17396  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
17397  * (defaults to undefined with no checkbox rendered)
17398  * @constructor
17399  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
17400  */
17401 Roo.tree.TreeNode = function(attributes){
17402     attributes = attributes || {};
17403     if(typeof attributes == "string"){
17404         attributes = {text: attributes};
17405     }
17406     this.childrenRendered = false;
17407     this.rendered = false;
17408     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
17409     this.expanded = attributes.expanded === true;
17410     this.isTarget = attributes.isTarget !== false;
17411     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
17412     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
17413
17414     /**
17415      * Read-only. The text for this node. To change it use setText().
17416      * @type String
17417      */
17418     this.text = attributes.text;
17419     /**
17420      * True if this node is disabled.
17421      * @type Boolean
17422      */
17423     this.disabled = attributes.disabled === true;
17424
17425     this.addEvents({
17426         /**
17427         * @event textchange
17428         * Fires when the text for this node is changed
17429         * @param {Node} this This node
17430         * @param {String} text The new text
17431         * @param {String} oldText The old text
17432         */
17433         "textchange" : true,
17434         /**
17435         * @event beforeexpand
17436         * Fires before this node is expanded, return false to cancel.
17437         * @param {Node} this This node
17438         * @param {Boolean} deep
17439         * @param {Boolean} anim
17440         */
17441         "beforeexpand" : true,
17442         /**
17443         * @event beforecollapse
17444         * Fires before this node is collapsed, return false to cancel.
17445         * @param {Node} this This node
17446         * @param {Boolean} deep
17447         * @param {Boolean} anim
17448         */
17449         "beforecollapse" : true,
17450         /**
17451         * @event expand
17452         * Fires when this node is expanded
17453         * @param {Node} this This node
17454         */
17455         "expand" : true,
17456         /**
17457         * @event disabledchange
17458         * Fires when the disabled status of this node changes
17459         * @param {Node} this This node
17460         * @param {Boolean} disabled
17461         */
17462         "disabledchange" : true,
17463         /**
17464         * @event collapse
17465         * Fires when this node is collapsed
17466         * @param {Node} this This node
17467         */
17468         "collapse" : true,
17469         /**
17470         * @event beforeclick
17471         * Fires before click processing. Return false to cancel the default action.
17472         * @param {Node} this This node
17473         * @param {Roo.EventObject} e The event object
17474         */
17475         "beforeclick":true,
17476         /**
17477         * @event checkchange
17478         * Fires when a node with a checkbox's checked property changes
17479         * @param {Node} this This node
17480         * @param {Boolean} checked
17481         */
17482         "checkchange":true,
17483         /**
17484         * @event click
17485         * Fires when this node is clicked
17486         * @param {Node} this This node
17487         * @param {Roo.EventObject} e The event object
17488         */
17489         "click":true,
17490         /**
17491         * @event dblclick
17492         * Fires when this node is double clicked
17493         * @param {Node} this This node
17494         * @param {Roo.EventObject} e The event object
17495         */
17496         "dblclick":true,
17497         /**
17498         * @event contextmenu
17499         * Fires when this node is right clicked
17500         * @param {Node} this This node
17501         * @param {Roo.EventObject} e The event object
17502         */
17503         "contextmenu":true,
17504         /**
17505         * @event beforechildrenrendered
17506         * Fires right before the child nodes for this node are rendered
17507         * @param {Node} this This node
17508         */
17509         "beforechildrenrendered":true
17510     });
17511
17512     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
17513
17514     /**
17515      * Read-only. The UI for this node
17516      * @type TreeNodeUI
17517      */
17518     this.ui = new uiClass(this);
17519     
17520     // finally support items[]
17521     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
17522         return;
17523     }
17524     
17525     
17526     Roo.each(this.attributes.items, function(c) {
17527         this.appendChild(Roo.factory(c,Roo.Tree));
17528     }, this);
17529     delete this.attributes.items;
17530     
17531     
17532     
17533 };
17534 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
17535     preventHScroll: true,
17536     /**
17537      * Returns true if this node is expanded
17538      * @return {Boolean}
17539      */
17540     isExpanded : function(){
17541         return this.expanded;
17542     },
17543
17544     /**
17545      * Returns the UI object for this node
17546      * @return {TreeNodeUI}
17547      */
17548     getUI : function(){
17549         return this.ui;
17550     },
17551
17552     // private override
17553     setFirstChild : function(node){
17554         var of = this.firstChild;
17555         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
17556         if(this.childrenRendered && of && node != of){
17557             of.renderIndent(true, true);
17558         }
17559         if(this.rendered){
17560             this.renderIndent(true, true);
17561         }
17562     },
17563
17564     // private override
17565     setLastChild : function(node){
17566         var ol = this.lastChild;
17567         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
17568         if(this.childrenRendered && ol && node != ol){
17569             ol.renderIndent(true, true);
17570         }
17571         if(this.rendered){
17572             this.renderIndent(true, true);
17573         }
17574     },
17575
17576     // these methods are overridden to provide lazy rendering support
17577     // private override
17578     appendChild : function()
17579     {
17580         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
17581         if(node && this.childrenRendered){
17582             node.render();
17583         }
17584         this.ui.updateExpandIcon();
17585         return node;
17586     },
17587
17588     // private override
17589     removeChild : function(node){
17590         this.ownerTree.getSelectionModel().unselect(node);
17591         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
17592         // if it's been rendered remove dom node
17593         if(this.childrenRendered){
17594             node.ui.remove();
17595         }
17596         if(this.childNodes.length < 1){
17597             this.collapse(false, false);
17598         }else{
17599             this.ui.updateExpandIcon();
17600         }
17601         if(!this.firstChild) {
17602             this.childrenRendered = false;
17603         }
17604         return node;
17605     },
17606
17607     // private override
17608     insertBefore : function(node, refNode){
17609         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
17610         if(newNode && refNode && this.childrenRendered){
17611             node.render();
17612         }
17613         this.ui.updateExpandIcon();
17614         return newNode;
17615     },
17616
17617     /**
17618      * Sets the text for this node
17619      * @param {String} text
17620      */
17621     setText : function(text){
17622         var oldText = this.text;
17623         this.text = text;
17624         this.attributes.text = text;
17625         if(this.rendered){ // event without subscribing
17626             this.ui.onTextChange(this, text, oldText);
17627         }
17628         this.fireEvent("textchange", this, text, oldText);
17629     },
17630
17631     /**
17632      * Triggers selection of this node
17633      */
17634     select : function(){
17635         this.getOwnerTree().getSelectionModel().select(this);
17636     },
17637
17638     /**
17639      * Triggers deselection of this node
17640      */
17641     unselect : function(){
17642         this.getOwnerTree().getSelectionModel().unselect(this);
17643     },
17644
17645     /**
17646      * Returns true if this node is selected
17647      * @return {Boolean}
17648      */
17649     isSelected : function(){
17650         return this.getOwnerTree().getSelectionModel().isSelected(this);
17651     },
17652
17653     /**
17654      * Expand this node.
17655      * @param {Boolean} deep (optional) True to expand all children as well
17656      * @param {Boolean} anim (optional) false to cancel the default animation
17657      * @param {Function} callback (optional) A callback to be called when
17658      * expanding this node completes (does not wait for deep expand to complete).
17659      * Called with 1 parameter, this node.
17660      */
17661     expand : function(deep, anim, callback){
17662         if(!this.expanded){
17663             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
17664                 return;
17665             }
17666             if(!this.childrenRendered){
17667                 this.renderChildren();
17668             }
17669             this.expanded = true;
17670             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17671                 this.ui.animExpand(function(){
17672                     this.fireEvent("expand", this);
17673                     if(typeof callback == "function"){
17674                         callback(this);
17675                     }
17676                     if(deep === true){
17677                         this.expandChildNodes(true);
17678                     }
17679                 }.createDelegate(this));
17680                 return;
17681             }else{
17682                 this.ui.expand();
17683                 this.fireEvent("expand", this);
17684                 if(typeof callback == "function"){
17685                     callback(this);
17686                 }
17687             }
17688         }else{
17689            if(typeof callback == "function"){
17690                callback(this);
17691            }
17692         }
17693         if(deep === true){
17694             this.expandChildNodes(true);
17695         }
17696     },
17697
17698     isHiddenRoot : function(){
17699         return this.isRoot && !this.getOwnerTree().rootVisible;
17700     },
17701
17702     /**
17703      * Collapse this node.
17704      * @param {Boolean} deep (optional) True to collapse all children as well
17705      * @param {Boolean} anim (optional) false to cancel the default animation
17706      */
17707     collapse : function(deep, anim){
17708         if(this.expanded && !this.isHiddenRoot()){
17709             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17710                 return;
17711             }
17712             this.expanded = false;
17713             if((this.getOwnerTree().animate && anim !== false) || anim){
17714                 this.ui.animCollapse(function(){
17715                     this.fireEvent("collapse", this);
17716                     if(deep === true){
17717                         this.collapseChildNodes(true);
17718                     }
17719                 }.createDelegate(this));
17720                 return;
17721             }else{
17722                 this.ui.collapse();
17723                 this.fireEvent("collapse", this);
17724             }
17725         }
17726         if(deep === true){
17727             var cs = this.childNodes;
17728             for(var i = 0, len = cs.length; i < len; i++) {
17729                 cs[i].collapse(true, false);
17730             }
17731         }
17732     },
17733
17734     // private
17735     delayedExpand : function(delay){
17736         if(!this.expandProcId){
17737             this.expandProcId = this.expand.defer(delay, this);
17738         }
17739     },
17740
17741     // private
17742     cancelExpand : function(){
17743         if(this.expandProcId){
17744             clearTimeout(this.expandProcId);
17745         }
17746         this.expandProcId = false;
17747     },
17748
17749     /**
17750      * Toggles expanded/collapsed state of the node
17751      */
17752     toggle : function(){
17753         if(this.expanded){
17754             this.collapse();
17755         }else{
17756             this.expand();
17757         }
17758     },
17759
17760     /**
17761      * Ensures all parent nodes are expanded
17762      */
17763     ensureVisible : function(callback){
17764         var tree = this.getOwnerTree();
17765         tree.expandPath(this.parentNode.getPath(), false, function(){
17766             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17767             Roo.callback(callback);
17768         }.createDelegate(this));
17769     },
17770
17771     /**
17772      * Expand all child nodes
17773      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17774      */
17775     expandChildNodes : function(deep){
17776         var cs = this.childNodes;
17777         for(var i = 0, len = cs.length; i < len; i++) {
17778                 cs[i].expand(deep);
17779         }
17780     },
17781
17782     /**
17783      * Collapse all child nodes
17784      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17785      */
17786     collapseChildNodes : function(deep){
17787         var cs = this.childNodes;
17788         for(var i = 0, len = cs.length; i < len; i++) {
17789                 cs[i].collapse(deep);
17790         }
17791     },
17792
17793     /**
17794      * Disables this node
17795      */
17796     disable : function(){
17797         this.disabled = true;
17798         this.unselect();
17799         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17800             this.ui.onDisableChange(this, true);
17801         }
17802         this.fireEvent("disabledchange", this, true);
17803     },
17804
17805     /**
17806      * Enables this node
17807      */
17808     enable : function(){
17809         this.disabled = false;
17810         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17811             this.ui.onDisableChange(this, false);
17812         }
17813         this.fireEvent("disabledchange", this, false);
17814     },
17815
17816     // private
17817     renderChildren : function(suppressEvent){
17818         if(suppressEvent !== false){
17819             this.fireEvent("beforechildrenrendered", this);
17820         }
17821         var cs = this.childNodes;
17822         for(var i = 0, len = cs.length; i < len; i++){
17823             cs[i].render(true);
17824         }
17825         this.childrenRendered = true;
17826     },
17827
17828     // private
17829     sort : function(fn, scope){
17830         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17831         if(this.childrenRendered){
17832             var cs = this.childNodes;
17833             for(var i = 0, len = cs.length; i < len; i++){
17834                 cs[i].render(true);
17835             }
17836         }
17837     },
17838
17839     // private
17840     render : function(bulkRender){
17841         this.ui.render(bulkRender);
17842         if(!this.rendered){
17843             this.rendered = true;
17844             if(this.expanded){
17845                 this.expanded = false;
17846                 this.expand(false, false);
17847             }
17848         }
17849     },
17850
17851     // private
17852     renderIndent : function(deep, refresh){
17853         if(refresh){
17854             this.ui.childIndent = null;
17855         }
17856         this.ui.renderIndent();
17857         if(deep === true && this.childrenRendered){
17858             var cs = this.childNodes;
17859             for(var i = 0, len = cs.length; i < len; i++){
17860                 cs[i].renderIndent(true, refresh);
17861             }
17862         }
17863     }
17864 });/*
17865  * Based on:
17866  * Ext JS Library 1.1.1
17867  * Copyright(c) 2006-2007, Ext JS, LLC.
17868  *
17869  * Originally Released Under LGPL - original licence link has changed is not relivant.
17870  *
17871  * Fork - LGPL
17872  * <script type="text/javascript">
17873  */
17874  
17875 /**
17876  * @class Roo.tree.AsyncTreeNode
17877  * @extends Roo.tree.TreeNode
17878  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17879  * @constructor
17880  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
17881  */
17882  Roo.tree.AsyncTreeNode = function(config){
17883     this.loaded = false;
17884     this.loading = false;
17885     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17886     /**
17887     * @event beforeload
17888     * Fires before this node is loaded, return false to cancel
17889     * @param {Node} this This node
17890     */
17891     this.addEvents({'beforeload':true, 'load': true});
17892     /**
17893     * @event load
17894     * Fires when this node is loaded
17895     * @param {Node} this This node
17896     */
17897     /**
17898      * The loader used by this node (defaults to using the tree's defined loader)
17899      * @type TreeLoader
17900      * @property loader
17901      */
17902 };
17903 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17904     expand : function(deep, anim, callback){
17905         if(this.loading){ // if an async load is already running, waiting til it's done
17906             var timer;
17907             var f = function(){
17908                 if(!this.loading){ // done loading
17909                     clearInterval(timer);
17910                     this.expand(deep, anim, callback);
17911                 }
17912             }.createDelegate(this);
17913             timer = setInterval(f, 200);
17914             return;
17915         }
17916         if(!this.loaded){
17917             if(this.fireEvent("beforeload", this) === false){
17918                 return;
17919             }
17920             this.loading = true;
17921             this.ui.beforeLoad(this);
17922             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17923             if(loader){
17924                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17925                 return;
17926             }
17927         }
17928         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17929     },
17930     
17931     /**
17932      * Returns true if this node is currently loading
17933      * @return {Boolean}
17934      */
17935     isLoading : function(){
17936         return this.loading;  
17937     },
17938     
17939     loadComplete : function(deep, anim, callback){
17940         this.loading = false;
17941         this.loaded = true;
17942         this.ui.afterLoad(this);
17943         this.fireEvent("load", this);
17944         this.expand(deep, anim, callback);
17945     },
17946     
17947     /**
17948      * Returns true if this node has been loaded
17949      * @return {Boolean}
17950      */
17951     isLoaded : function(){
17952         return this.loaded;
17953     },
17954     
17955     hasChildNodes : function(){
17956         if(!this.isLeaf() && !this.loaded){
17957             return true;
17958         }else{
17959             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17960         }
17961     },
17962
17963     /**
17964      * Trigger a reload for this node
17965      * @param {Function} callback
17966      */
17967     reload : function(callback){
17968         this.collapse(false, false);
17969         while(this.firstChild){
17970             this.removeChild(this.firstChild);
17971         }
17972         this.childrenRendered = false;
17973         this.loaded = false;
17974         if(this.isHiddenRoot()){
17975             this.expanded = false;
17976         }
17977         this.expand(false, false, callback);
17978     }
17979 });/*
17980  * Based on:
17981  * Ext JS Library 1.1.1
17982  * Copyright(c) 2006-2007, Ext JS, LLC.
17983  *
17984  * Originally Released Under LGPL - original licence link has changed is not relivant.
17985  *
17986  * Fork - LGPL
17987  * <script type="text/javascript">
17988  */
17989  
17990 /**
17991  * @class Roo.tree.TreeNodeUI
17992  * @constructor
17993  * @param {Object} node The node to render
17994  * The TreeNode UI implementation is separate from the
17995  * tree implementation. Unless you are customizing the tree UI,
17996  * you should never have to use this directly.
17997  */
17998 Roo.tree.TreeNodeUI = function(node){
17999     this.node = node;
18000     this.rendered = false;
18001     this.animating = false;
18002     this.emptyIcon = Roo.BLANK_IMAGE_URL;
18003 };
18004
18005 Roo.tree.TreeNodeUI.prototype = {
18006     removeChild : function(node){
18007         if(this.rendered){
18008             this.ctNode.removeChild(node.ui.getEl());
18009         }
18010     },
18011
18012     beforeLoad : function(){
18013          this.addClass("x-tree-node-loading");
18014     },
18015
18016     afterLoad : function(){
18017          this.removeClass("x-tree-node-loading");
18018     },
18019
18020     onTextChange : function(node, text, oldText){
18021         if(this.rendered){
18022             this.textNode.innerHTML = text;
18023         }
18024     },
18025
18026     onDisableChange : function(node, state){
18027         this.disabled = state;
18028         if(state){
18029             this.addClass("x-tree-node-disabled");
18030         }else{
18031             this.removeClass("x-tree-node-disabled");
18032         }
18033     },
18034
18035     onSelectedChange : function(state){
18036         if(state){
18037             this.focus();
18038             this.addClass("x-tree-selected");
18039         }else{
18040             //this.blur();
18041             this.removeClass("x-tree-selected");
18042         }
18043     },
18044
18045     onMove : function(tree, node, oldParent, newParent, index, refNode){
18046         this.childIndent = null;
18047         if(this.rendered){
18048             var targetNode = newParent.ui.getContainer();
18049             if(!targetNode){//target not rendered
18050                 this.holder = document.createElement("div");
18051                 this.holder.appendChild(this.wrap);
18052                 return;
18053             }
18054             var insertBefore = refNode ? refNode.ui.getEl() : null;
18055             if(insertBefore){
18056                 targetNode.insertBefore(this.wrap, insertBefore);
18057             }else{
18058                 targetNode.appendChild(this.wrap);
18059             }
18060             this.node.renderIndent(true);
18061         }
18062     },
18063
18064     addClass : function(cls){
18065         if(this.elNode){
18066             Roo.fly(this.elNode).addClass(cls);
18067         }
18068     },
18069
18070     removeClass : function(cls){
18071         if(this.elNode){
18072             Roo.fly(this.elNode).removeClass(cls);
18073         }
18074     },
18075
18076     remove : function(){
18077         if(this.rendered){
18078             this.holder = document.createElement("div");
18079             this.holder.appendChild(this.wrap);
18080         }
18081     },
18082
18083     fireEvent : function(){
18084         return this.node.fireEvent.apply(this.node, arguments);
18085     },
18086
18087     initEvents : function(){
18088         this.node.on("move", this.onMove, this);
18089         var E = Roo.EventManager;
18090         var a = this.anchor;
18091
18092         var el = Roo.fly(a, '_treeui');
18093
18094         if(Roo.isOpera){ // opera render bug ignores the CSS
18095             el.setStyle("text-decoration", "none");
18096         }
18097
18098         el.on("click", this.onClick, this);
18099         el.on("dblclick", this.onDblClick, this);
18100
18101         if(this.checkbox){
18102             Roo.EventManager.on(this.checkbox,
18103                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
18104         }
18105
18106         el.on("contextmenu", this.onContextMenu, this);
18107
18108         var icon = Roo.fly(this.iconNode);
18109         icon.on("click", this.onClick, this);
18110         icon.on("dblclick", this.onDblClick, this);
18111         icon.on("contextmenu", this.onContextMenu, this);
18112         E.on(this.ecNode, "click", this.ecClick, this, true);
18113
18114         if(this.node.disabled){
18115             this.addClass("x-tree-node-disabled");
18116         }
18117         if(this.node.hidden){
18118             this.addClass("x-tree-node-disabled");
18119         }
18120         var ot = this.node.getOwnerTree();
18121         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
18122         if(dd && (!this.node.isRoot || ot.rootVisible)){
18123             Roo.dd.Registry.register(this.elNode, {
18124                 node: this.node,
18125                 handles: this.getDDHandles(),
18126                 isHandle: false
18127             });
18128         }
18129     },
18130
18131     getDDHandles : function(){
18132         return [this.iconNode, this.textNode];
18133     },
18134
18135     hide : function(){
18136         if(this.rendered){
18137             this.wrap.style.display = "none";
18138         }
18139     },
18140
18141     show : function(){
18142         if(this.rendered){
18143             this.wrap.style.display = "";
18144         }
18145     },
18146
18147     onContextMenu : function(e){
18148         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
18149             e.preventDefault();
18150             this.focus();
18151             this.fireEvent("contextmenu", this.node, e);
18152         }
18153     },
18154
18155     onClick : function(e){
18156         if(this.dropping){
18157             e.stopEvent();
18158             return;
18159         }
18160         if(this.fireEvent("beforeclick", this.node, e) !== false){
18161             if(!this.disabled && this.node.attributes.href){
18162                 this.fireEvent("click", this.node, e);
18163                 return;
18164             }
18165             e.preventDefault();
18166             if(this.disabled){
18167                 return;
18168             }
18169
18170             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
18171                 this.node.toggle();
18172             }
18173
18174             this.fireEvent("click", this.node, e);
18175         }else{
18176             e.stopEvent();
18177         }
18178     },
18179
18180     onDblClick : function(e){
18181         e.preventDefault();
18182         if(this.disabled){
18183             return;
18184         }
18185         if(this.checkbox){
18186             this.toggleCheck();
18187         }
18188         if(!this.animating && this.node.hasChildNodes()){
18189             this.node.toggle();
18190         }
18191         this.fireEvent("dblclick", this.node, e);
18192     },
18193
18194     onCheckChange : function(){
18195         var checked = this.checkbox.checked;
18196         this.node.attributes.checked = checked;
18197         this.fireEvent('checkchange', this.node, checked);
18198     },
18199
18200     ecClick : function(e){
18201         if(!this.animating && this.node.hasChildNodes()){
18202             this.node.toggle();
18203         }
18204     },
18205
18206     startDrop : function(){
18207         this.dropping = true;
18208     },
18209
18210     // delayed drop so the click event doesn't get fired on a drop
18211     endDrop : function(){
18212        setTimeout(function(){
18213            this.dropping = false;
18214        }.createDelegate(this), 50);
18215     },
18216
18217     expand : function(){
18218         this.updateExpandIcon();
18219         this.ctNode.style.display = "";
18220     },
18221
18222     focus : function(){
18223         if(!this.node.preventHScroll){
18224             try{this.anchor.focus();
18225             }catch(e){}
18226         }else if(!Roo.isIE){
18227             try{
18228                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
18229                 var l = noscroll.scrollLeft;
18230                 this.anchor.focus();
18231                 noscroll.scrollLeft = l;
18232             }catch(e){}
18233         }
18234     },
18235
18236     toggleCheck : function(value){
18237         var cb = this.checkbox;
18238         if(cb){
18239             cb.checked = (value === undefined ? !cb.checked : value);
18240         }
18241     },
18242
18243     blur : function(){
18244         try{
18245             this.anchor.blur();
18246         }catch(e){}
18247     },
18248
18249     animExpand : function(callback){
18250         var ct = Roo.get(this.ctNode);
18251         ct.stopFx();
18252         if(!this.node.hasChildNodes()){
18253             this.updateExpandIcon();
18254             this.ctNode.style.display = "";
18255             Roo.callback(callback);
18256             return;
18257         }
18258         this.animating = true;
18259         this.updateExpandIcon();
18260
18261         ct.slideIn('t', {
18262            callback : function(){
18263                this.animating = false;
18264                Roo.callback(callback);
18265             },
18266             scope: this,
18267             duration: this.node.ownerTree.duration || .25
18268         });
18269     },
18270
18271     highlight : function(){
18272         var tree = this.node.getOwnerTree();
18273         Roo.fly(this.wrap).highlight(
18274             tree.hlColor || "C3DAF9",
18275             {endColor: tree.hlBaseColor}
18276         );
18277     },
18278
18279     collapse : function(){
18280         this.updateExpandIcon();
18281         this.ctNode.style.display = "none";
18282     },
18283
18284     animCollapse : function(callback){
18285         var ct = Roo.get(this.ctNode);
18286         ct.enableDisplayMode('block');
18287         ct.stopFx();
18288
18289         this.animating = true;
18290         this.updateExpandIcon();
18291
18292         ct.slideOut('t', {
18293             callback : function(){
18294                this.animating = false;
18295                Roo.callback(callback);
18296             },
18297             scope: this,
18298             duration: this.node.ownerTree.duration || .25
18299         });
18300     },
18301
18302     getContainer : function(){
18303         return this.ctNode;
18304     },
18305
18306     getEl : function(){
18307         return this.wrap;
18308     },
18309
18310     appendDDGhost : function(ghostNode){
18311         ghostNode.appendChild(this.elNode.cloneNode(true));
18312     },
18313
18314     getDDRepairXY : function(){
18315         return Roo.lib.Dom.getXY(this.iconNode);
18316     },
18317
18318     onRender : function(){
18319         this.render();
18320     },
18321
18322     render : function(bulkRender){
18323         var n = this.node, a = n.attributes;
18324         var targetNode = n.parentNode ?
18325               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
18326
18327         if(!this.rendered){
18328             this.rendered = true;
18329
18330             this.renderElements(n, a, targetNode, bulkRender);
18331
18332             if(a.qtip){
18333                if(this.textNode.setAttributeNS){
18334                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
18335                    if(a.qtipTitle){
18336                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
18337                    }
18338                }else{
18339                    this.textNode.setAttribute("ext:qtip", a.qtip);
18340                    if(a.qtipTitle){
18341                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
18342                    }
18343                }
18344             }else if(a.qtipCfg){
18345                 a.qtipCfg.target = Roo.id(this.textNode);
18346                 Roo.QuickTips.register(a.qtipCfg);
18347             }
18348             this.initEvents();
18349             if(!this.node.expanded){
18350                 this.updateExpandIcon();
18351             }
18352         }else{
18353             if(bulkRender === true) {
18354                 targetNode.appendChild(this.wrap);
18355             }
18356         }
18357     },
18358
18359     renderElements : function(n, a, targetNode, bulkRender)
18360     {
18361         // add some indent caching, this helps performance when rendering a large tree
18362         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18363         var t = n.getOwnerTree();
18364         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
18365         if (typeof(n.attributes.html) != 'undefined') {
18366             txt = n.attributes.html;
18367         }
18368         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
18369         var cb = typeof a.checked == 'boolean';
18370         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18371         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
18372             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
18373             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
18374             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
18375             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
18376             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
18377              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
18378                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
18379             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18380             "</li>"];
18381
18382         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18383             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18384                                 n.nextSibling.ui.getEl(), buf.join(""));
18385         }else{
18386             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18387         }
18388
18389         this.elNode = this.wrap.childNodes[0];
18390         this.ctNode = this.wrap.childNodes[1];
18391         var cs = this.elNode.childNodes;
18392         this.indentNode = cs[0];
18393         this.ecNode = cs[1];
18394         this.iconNode = cs[2];
18395         var index = 3;
18396         if(cb){
18397             this.checkbox = cs[3];
18398             index++;
18399         }
18400         this.anchor = cs[index];
18401         this.textNode = cs[index].firstChild;
18402     },
18403
18404     getAnchor : function(){
18405         return this.anchor;
18406     },
18407
18408     getTextEl : function(){
18409         return this.textNode;
18410     },
18411
18412     getIconEl : function(){
18413         return this.iconNode;
18414     },
18415
18416     isChecked : function(){
18417         return this.checkbox ? this.checkbox.checked : false;
18418     },
18419
18420     updateExpandIcon : function(){
18421         if(this.rendered){
18422             var n = this.node, c1, c2;
18423             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
18424             var hasChild = n.hasChildNodes();
18425             if(hasChild){
18426                 if(n.expanded){
18427                     cls += "-minus";
18428                     c1 = "x-tree-node-collapsed";
18429                     c2 = "x-tree-node-expanded";
18430                 }else{
18431                     cls += "-plus";
18432                     c1 = "x-tree-node-expanded";
18433                     c2 = "x-tree-node-collapsed";
18434                 }
18435                 if(this.wasLeaf){
18436                     this.removeClass("x-tree-node-leaf");
18437                     this.wasLeaf = false;
18438                 }
18439                 if(this.c1 != c1 || this.c2 != c2){
18440                     Roo.fly(this.elNode).replaceClass(c1, c2);
18441                     this.c1 = c1; this.c2 = c2;
18442                 }
18443             }else{
18444                 // this changes non-leafs into leafs if they have no children.
18445                 // it's not very rational behaviour..
18446                 
18447                 if(!this.wasLeaf && this.node.leaf){
18448                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
18449                     delete this.c1;
18450                     delete this.c2;
18451                     this.wasLeaf = true;
18452                 }
18453             }
18454             var ecc = "x-tree-ec-icon "+cls;
18455             if(this.ecc != ecc){
18456                 this.ecNode.className = ecc;
18457                 this.ecc = ecc;
18458             }
18459         }
18460     },
18461
18462     getChildIndent : function(){
18463         if(!this.childIndent){
18464             var buf = [];
18465             var p = this.node;
18466             while(p){
18467                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
18468                     if(!p.isLast()) {
18469                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
18470                     } else {
18471                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
18472                     }
18473                 }
18474                 p = p.parentNode;
18475             }
18476             this.childIndent = buf.join("");
18477         }
18478         return this.childIndent;
18479     },
18480
18481     renderIndent : function(){
18482         if(this.rendered){
18483             var indent = "";
18484             var p = this.node.parentNode;
18485             if(p){
18486                 indent = p.ui.getChildIndent();
18487             }
18488             if(this.indentMarkup != indent){ // don't rerender if not required
18489                 this.indentNode.innerHTML = indent;
18490                 this.indentMarkup = indent;
18491             }
18492             this.updateExpandIcon();
18493         }
18494     }
18495 };
18496
18497 Roo.tree.RootTreeNodeUI = function(){
18498     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
18499 };
18500 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
18501     render : function(){
18502         if(!this.rendered){
18503             var targetNode = this.node.ownerTree.innerCt.dom;
18504             this.node.expanded = true;
18505             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
18506             this.wrap = this.ctNode = targetNode.firstChild;
18507         }
18508     },
18509     collapse : function(){
18510     },
18511     expand : function(){
18512     }
18513 });/*
18514  * Based on:
18515  * Ext JS Library 1.1.1
18516  * Copyright(c) 2006-2007, Ext JS, LLC.
18517  *
18518  * Originally Released Under LGPL - original licence link has changed is not relivant.
18519  *
18520  * Fork - LGPL
18521  * <script type="text/javascript">
18522  */
18523 /**
18524  * @class Roo.tree.TreeLoader
18525  * @extends Roo.util.Observable
18526  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
18527  * nodes from a specified URL. The response must be a javascript Array definition
18528  * who's elements are node definition objects. eg:
18529  * <pre><code>
18530 {  success : true,
18531    data :      [
18532    
18533     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
18534     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
18535     ]
18536 }
18537
18538
18539 </code></pre>
18540  * <br><br>
18541  * The old style respose with just an array is still supported, but not recommended.
18542  * <br><br>
18543  *
18544  * A server request is sent, and child nodes are loaded only when a node is expanded.
18545  * The loading node's id is passed to the server under the parameter name "node" to
18546  * enable the server to produce the correct child nodes.
18547  * <br><br>
18548  * To pass extra parameters, an event handler may be attached to the "beforeload"
18549  * event, and the parameters specified in the TreeLoader's baseParams property:
18550  * <pre><code>
18551     myTreeLoader.on("beforeload", function(treeLoader, node) {
18552         this.baseParams.category = node.attributes.category;
18553     }, this);
18554 </code></pre><
18555  * This would pass an HTTP parameter called "category" to the server containing
18556  * the value of the Node's "category" attribute.
18557  * @constructor
18558  * Creates a new Treeloader.
18559  * @param {Object} config A config object containing config properties.
18560  */
18561 Roo.tree.TreeLoader = function(config){
18562     this.baseParams = {};
18563     this.requestMethod = "POST";
18564     Roo.apply(this, config);
18565
18566     this.addEvents({
18567     
18568         /**
18569          * @event beforeload
18570          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
18571          * @param {Object} This TreeLoader object.
18572          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18573          * @param {Object} callback The callback function specified in the {@link #load} call.
18574          */
18575         beforeload : true,
18576         /**
18577          * @event load
18578          * Fires when the node has been successfuly loaded.
18579          * @param {Object} This TreeLoader object.
18580          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18581          * @param {Object} response The response object containing the data from the server.
18582          */
18583         load : true,
18584         /**
18585          * @event loadexception
18586          * Fires if the network request failed.
18587          * @param {Object} This TreeLoader object.
18588          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18589          * @param {Object} response The response object containing the data from the server.
18590          */
18591         loadexception : true,
18592         /**
18593          * @event create
18594          * Fires before a node is created, enabling you to return custom Node types 
18595          * @param {Object} This TreeLoader object.
18596          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
18597          */
18598         create : true
18599     });
18600
18601     Roo.tree.TreeLoader.superclass.constructor.call(this);
18602 };
18603
18604 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
18605     /**
18606     * @cfg {String} dataUrl The URL from which to request a Json string which
18607     * specifies an array of node definition object representing the child nodes
18608     * to be loaded.
18609     */
18610     /**
18611     * @cfg {String} requestMethod either GET or POST
18612     * defaults to POST (due to BC)
18613     * to be loaded.
18614     */
18615     /**
18616     * @cfg {Object} baseParams (optional) An object containing properties which
18617     * specify HTTP parameters to be passed to each request for child nodes.
18618     */
18619     /**
18620     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
18621     * created by this loader. If the attributes sent by the server have an attribute in this object,
18622     * they take priority.
18623     */
18624     /**
18625     * @cfg {Object} uiProviders (optional) An object containing properties which
18626     * 
18627     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
18628     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
18629     * <i>uiProvider</i> attribute of a returned child node is a string rather
18630     * than a reference to a TreeNodeUI implementation, this that string value
18631     * is used as a property name in the uiProviders object. You can define the provider named
18632     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
18633     */
18634     uiProviders : {},
18635
18636     /**
18637     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
18638     * child nodes before loading.
18639     */
18640     clearOnLoad : true,
18641
18642     /**
18643     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
18644     * property on loading, rather than expecting an array. (eg. more compatible to a standard
18645     * Grid query { data : [ .....] }
18646     */
18647     
18648     root : false,
18649      /**
18650     * @cfg {String} queryParam (optional) 
18651     * Name of the query as it will be passed on the querystring (defaults to 'node')
18652     * eg. the request will be ?node=[id]
18653     */
18654     
18655     
18656     queryParam: false,
18657     
18658     /**
18659      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
18660      * This is called automatically when a node is expanded, but may be used to reload
18661      * a node (or append new children if the {@link #clearOnLoad} option is false.)
18662      * @param {Roo.tree.TreeNode} node
18663      * @param {Function} callback
18664      */
18665     load : function(node, callback){
18666         if(this.clearOnLoad){
18667             while(node.firstChild){
18668                 node.removeChild(node.firstChild);
18669             }
18670         }
18671         if(node.attributes.children){ // preloaded json children
18672             var cs = node.attributes.children;
18673             for(var i = 0, len = cs.length; i < len; i++){
18674                 node.appendChild(this.createNode(cs[i]));
18675             }
18676             if(typeof callback == "function"){
18677                 callback();
18678             }
18679         }else if(this.dataUrl){
18680             this.requestData(node, callback);
18681         }
18682     },
18683
18684     getParams: function(node){
18685         var buf = [], bp = this.baseParams;
18686         for(var key in bp){
18687             if(typeof bp[key] != "function"){
18688                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18689             }
18690         }
18691         var n = this.queryParam === false ? 'node' : this.queryParam;
18692         buf.push(n + "=", encodeURIComponent(node.id));
18693         return buf.join("");
18694     },
18695
18696     requestData : function(node, callback){
18697         if(this.fireEvent("beforeload", this, node, callback) !== false){
18698             this.transId = Roo.Ajax.request({
18699                 method:this.requestMethod,
18700                 url: this.dataUrl||this.url,
18701                 success: this.handleResponse,
18702                 failure: this.handleFailure,
18703                 scope: this,
18704                 argument: {callback: callback, node: node},
18705                 params: this.getParams(node)
18706             });
18707         }else{
18708             // if the load is cancelled, make sure we notify
18709             // the node that we are done
18710             if(typeof callback == "function"){
18711                 callback();
18712             }
18713         }
18714     },
18715
18716     isLoading : function(){
18717         return this.transId ? true : false;
18718     },
18719
18720     abort : function(){
18721         if(this.isLoading()){
18722             Roo.Ajax.abort(this.transId);
18723         }
18724     },
18725
18726     // private
18727     createNode : function(attr)
18728     {
18729         // apply baseAttrs, nice idea Corey!
18730         if(this.baseAttrs){
18731             Roo.applyIf(attr, this.baseAttrs);
18732         }
18733         if(this.applyLoader !== false){
18734             attr.loader = this;
18735         }
18736         // uiProvider = depreciated..
18737         
18738         if(typeof(attr.uiProvider) == 'string'){
18739            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
18740                 /**  eval:var:attr */ eval(attr.uiProvider);
18741         }
18742         if(typeof(this.uiProviders['default']) != 'undefined') {
18743             attr.uiProvider = this.uiProviders['default'];
18744         }
18745         
18746         this.fireEvent('create', this, attr);
18747         
18748         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18749         return(attr.leaf ?
18750                         new Roo.tree.TreeNode(attr) :
18751                         new Roo.tree.AsyncTreeNode(attr));
18752     },
18753
18754     processResponse : function(response, node, callback)
18755     {
18756         var json = response.responseText;
18757         try {
18758             
18759             var o = Roo.decode(json);
18760             
18761             if (this.root === false && typeof(o.success) != undefined) {
18762                 this.root = 'data'; // the default behaviour for list like data..
18763                 }
18764                 
18765             if (this.root !== false &&  !o.success) {
18766                 // it's a failure condition.
18767                 var a = response.argument;
18768                 this.fireEvent("loadexception", this, a.node, response);
18769                 Roo.log("Load failed - should have a handler really");
18770                 return;
18771             }
18772             
18773             
18774             
18775             if (this.root !== false) {
18776                  o = o[this.root];
18777             }
18778             
18779             for(var i = 0, len = o.length; i < len; i++){
18780                 var n = this.createNode(o[i]);
18781                 if(n){
18782                     node.appendChild(n);
18783                 }
18784             }
18785             if(typeof callback == "function"){
18786                 callback(this, node);
18787             }
18788         }catch(e){
18789             this.handleFailure(response);
18790         }
18791     },
18792
18793     handleResponse : function(response){
18794         this.transId = false;
18795         var a = response.argument;
18796         this.processResponse(response, a.node, a.callback);
18797         this.fireEvent("load", this, a.node, response);
18798     },
18799
18800     handleFailure : function(response)
18801     {
18802         // should handle failure better..
18803         this.transId = false;
18804         var a = response.argument;
18805         this.fireEvent("loadexception", this, a.node, response);
18806         if(typeof a.callback == "function"){
18807             a.callback(this, a.node);
18808         }
18809     }
18810 });/*
18811  * Based on:
18812  * Ext JS Library 1.1.1
18813  * Copyright(c) 2006-2007, Ext JS, LLC.
18814  *
18815  * Originally Released Under LGPL - original licence link has changed is not relivant.
18816  *
18817  * Fork - LGPL
18818  * <script type="text/javascript">
18819  */
18820
18821 /**
18822 * @class Roo.tree.TreeFilter
18823 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18824 * @param {TreePanel} tree
18825 * @param {Object} config (optional)
18826  */
18827 Roo.tree.TreeFilter = function(tree, config){
18828     this.tree = tree;
18829     this.filtered = {};
18830     Roo.apply(this, config);
18831 };
18832
18833 Roo.tree.TreeFilter.prototype = {
18834     clearBlank:false,
18835     reverse:false,
18836     autoClear:false,
18837     remove:false,
18838
18839      /**
18840      * Filter the data by a specific attribute.
18841      * @param {String/RegExp} value Either string that the attribute value
18842      * should start with or a RegExp to test against the attribute
18843      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18844      * @param {TreeNode} startNode (optional) The node to start the filter at.
18845      */
18846     filter : function(value, attr, startNode){
18847         attr = attr || "text";
18848         var f;
18849         if(typeof value == "string"){
18850             var vlen = value.length;
18851             // auto clear empty filter
18852             if(vlen == 0 && this.clearBlank){
18853                 this.clear();
18854                 return;
18855             }
18856             value = value.toLowerCase();
18857             f = function(n){
18858                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18859             };
18860         }else if(value.exec){ // regex?
18861             f = function(n){
18862                 return value.test(n.attributes[attr]);
18863             };
18864         }else{
18865             throw 'Illegal filter type, must be string or regex';
18866         }
18867         this.filterBy(f, null, startNode);
18868         },
18869
18870     /**
18871      * Filter by a function. The passed function will be called with each
18872      * node in the tree (or from the startNode). If the function returns true, the node is kept
18873      * otherwise it is filtered. If a node is filtered, its children are also filtered.
18874      * @param {Function} fn The filter function
18875      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18876      */
18877     filterBy : function(fn, scope, startNode){
18878         startNode = startNode || this.tree.root;
18879         if(this.autoClear){
18880             this.clear();
18881         }
18882         var af = this.filtered, rv = this.reverse;
18883         var f = function(n){
18884             if(n == startNode){
18885                 return true;
18886             }
18887             if(af[n.id]){
18888                 return false;
18889             }
18890             var m = fn.call(scope || n, n);
18891             if(!m || rv){
18892                 af[n.id] = n;
18893                 n.ui.hide();
18894                 return false;
18895             }
18896             return true;
18897         };
18898         startNode.cascade(f);
18899         if(this.remove){
18900            for(var id in af){
18901                if(typeof id != "function"){
18902                    var n = af[id];
18903                    if(n && n.parentNode){
18904                        n.parentNode.removeChild(n);
18905                    }
18906                }
18907            }
18908         }
18909     },
18910
18911     /**
18912      * Clears the current filter. Note: with the "remove" option
18913      * set a filter cannot be cleared.
18914      */
18915     clear : function(){
18916         var t = this.tree;
18917         var af = this.filtered;
18918         for(var id in af){
18919             if(typeof id != "function"){
18920                 var n = af[id];
18921                 if(n){
18922                     n.ui.show();
18923                 }
18924             }
18925         }
18926         this.filtered = {};
18927     }
18928 };
18929 /*
18930  * Based on:
18931  * Ext JS Library 1.1.1
18932  * Copyright(c) 2006-2007, Ext JS, LLC.
18933  *
18934  * Originally Released Under LGPL - original licence link has changed is not relivant.
18935  *
18936  * Fork - LGPL
18937  * <script type="text/javascript">
18938  */
18939  
18940
18941 /**
18942  * @class Roo.tree.TreeSorter
18943  * Provides sorting of nodes in a TreePanel
18944  * 
18945  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18946  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18947  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18948  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18949  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18950  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18951  * @constructor
18952  * @param {TreePanel} tree
18953  * @param {Object} config
18954  */
18955 Roo.tree.TreeSorter = function(tree, config){
18956     Roo.apply(this, config);
18957     tree.on("beforechildrenrendered", this.doSort, this);
18958     tree.on("append", this.updateSort, this);
18959     tree.on("insert", this.updateSort, this);
18960     
18961     var dsc = this.dir && this.dir.toLowerCase() == "desc";
18962     var p = this.property || "text";
18963     var sortType = this.sortType;
18964     var fs = this.folderSort;
18965     var cs = this.caseSensitive === true;
18966     var leafAttr = this.leafAttr || 'leaf';
18967
18968     this.sortFn = function(n1, n2){
18969         if(fs){
18970             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18971                 return 1;
18972             }
18973             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
18974                 return -1;
18975             }
18976         }
18977         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
18978         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
18979         if(v1 < v2){
18980                         return dsc ? +1 : -1;
18981                 }else if(v1 > v2){
18982                         return dsc ? -1 : +1;
18983         }else{
18984                 return 0;
18985         }
18986     };
18987 };
18988
18989 Roo.tree.TreeSorter.prototype = {
18990     doSort : function(node){
18991         node.sort(this.sortFn);
18992     },
18993     
18994     compareNodes : function(n1, n2){
18995         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
18996     },
18997     
18998     updateSort : function(tree, node){
18999         if(node.childrenRendered){
19000             this.doSort.defer(1, this, [node]);
19001         }
19002     }
19003 };/*
19004  * Based on:
19005  * Ext JS Library 1.1.1
19006  * Copyright(c) 2006-2007, Ext JS, LLC.
19007  *
19008  * Originally Released Under LGPL - original licence link has changed is not relivant.
19009  *
19010  * Fork - LGPL
19011  * <script type="text/javascript">
19012  */
19013
19014 if(Roo.dd.DropZone){
19015     
19016 Roo.tree.TreeDropZone = function(tree, config){
19017     this.allowParentInsert = false;
19018     this.allowContainerDrop = false;
19019     this.appendOnly = false;
19020     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
19021     this.tree = tree;
19022     this.lastInsertClass = "x-tree-no-status";
19023     this.dragOverData = {};
19024 };
19025
19026 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
19027     ddGroup : "TreeDD",
19028     scroll:  true,
19029     
19030     expandDelay : 1000,
19031     
19032     expandNode : function(node){
19033         if(node.hasChildNodes() && !node.isExpanded()){
19034             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
19035         }
19036     },
19037     
19038     queueExpand : function(node){
19039         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
19040     },
19041     
19042     cancelExpand : function(){
19043         if(this.expandProcId){
19044             clearTimeout(this.expandProcId);
19045             this.expandProcId = false;
19046         }
19047     },
19048     
19049     isValidDropPoint : function(n, pt, dd, e, data){
19050         if(!n || !data){ return false; }
19051         var targetNode = n.node;
19052         var dropNode = data.node;
19053         // default drop rules
19054         if(!(targetNode && targetNode.isTarget && pt)){
19055             return false;
19056         }
19057         if(pt == "append" && targetNode.allowChildren === false){
19058             return false;
19059         }
19060         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
19061             return false;
19062         }
19063         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
19064             return false;
19065         }
19066         // reuse the object
19067         var overEvent = this.dragOverData;
19068         overEvent.tree = this.tree;
19069         overEvent.target = targetNode;
19070         overEvent.data = data;
19071         overEvent.point = pt;
19072         overEvent.source = dd;
19073         overEvent.rawEvent = e;
19074         overEvent.dropNode = dropNode;
19075         overEvent.cancel = false;  
19076         var result = this.tree.fireEvent("nodedragover", overEvent);
19077         return overEvent.cancel === false && result !== false;
19078     },
19079     
19080     getDropPoint : function(e, n, dd)
19081     {
19082         var tn = n.node;
19083         if(tn.isRoot){
19084             return tn.allowChildren !== false ? "append" : false; // always append for root
19085         }
19086         var dragEl = n.ddel;
19087         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
19088         var y = Roo.lib.Event.getPageY(e);
19089         //var noAppend = tn.allowChildren === false || tn.isLeaf();
19090         
19091         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
19092         var noAppend = tn.allowChildren === false;
19093         if(this.appendOnly || tn.parentNode.allowChildren === false){
19094             return noAppend ? false : "append";
19095         }
19096         var noBelow = false;
19097         if(!this.allowParentInsert){
19098             noBelow = tn.hasChildNodes() && tn.isExpanded();
19099         }
19100         var q = (b - t) / (noAppend ? 2 : 3);
19101         if(y >= t && y < (t + q)){
19102             return "above";
19103         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
19104             return "below";
19105         }else{
19106             return "append";
19107         }
19108     },
19109     
19110     onNodeEnter : function(n, dd, e, data)
19111     {
19112         this.cancelExpand();
19113     },
19114     
19115     onNodeOver : function(n, dd, e, data)
19116     {
19117        
19118         var pt = this.getDropPoint(e, n, dd);
19119         var node = n.node;
19120         
19121         // auto node expand check
19122         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
19123             this.queueExpand(node);
19124         }else if(pt != "append"){
19125             this.cancelExpand();
19126         }
19127         
19128         // set the insert point style on the target node
19129         var returnCls = this.dropNotAllowed;
19130         if(this.isValidDropPoint(n, pt, dd, e, data)){
19131            if(pt){
19132                var el = n.ddel;
19133                var cls;
19134                if(pt == "above"){
19135                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
19136                    cls = "x-tree-drag-insert-above";
19137                }else if(pt == "below"){
19138                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
19139                    cls = "x-tree-drag-insert-below";
19140                }else{
19141                    returnCls = "x-tree-drop-ok-append";
19142                    cls = "x-tree-drag-append";
19143                }
19144                if(this.lastInsertClass != cls){
19145                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
19146                    this.lastInsertClass = cls;
19147                }
19148            }
19149        }
19150        return returnCls;
19151     },
19152     
19153     onNodeOut : function(n, dd, e, data){
19154         
19155         this.cancelExpand();
19156         this.removeDropIndicators(n);
19157     },
19158     
19159     onNodeDrop : function(n, dd, e, data){
19160         var point = this.getDropPoint(e, n, dd);
19161         var targetNode = n.node;
19162         targetNode.ui.startDrop();
19163         if(!this.isValidDropPoint(n, point, dd, e, data)){
19164             targetNode.ui.endDrop();
19165             return false;
19166         }
19167         // first try to find the drop node
19168         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
19169         var dropEvent = {
19170             tree : this.tree,
19171             target: targetNode,
19172             data: data,
19173             point: point,
19174             source: dd,
19175             rawEvent: e,
19176             dropNode: dropNode,
19177             cancel: !dropNode   
19178         };
19179         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
19180         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
19181             targetNode.ui.endDrop();
19182             return false;
19183         }
19184         // allow target changing
19185         targetNode = dropEvent.target;
19186         if(point == "append" && !targetNode.isExpanded()){
19187             targetNode.expand(false, null, function(){
19188                 this.completeDrop(dropEvent);
19189             }.createDelegate(this));
19190         }else{
19191             this.completeDrop(dropEvent);
19192         }
19193         return true;
19194     },
19195     
19196     completeDrop : function(de){
19197         var ns = de.dropNode, p = de.point, t = de.target;
19198         if(!(ns instanceof Array)){
19199             ns = [ns];
19200         }
19201         var n;
19202         for(var i = 0, len = ns.length; i < len; i++){
19203             n = ns[i];
19204             if(p == "above"){
19205                 t.parentNode.insertBefore(n, t);
19206             }else if(p == "below"){
19207                 t.parentNode.insertBefore(n, t.nextSibling);
19208             }else{
19209                 t.appendChild(n);
19210             }
19211         }
19212         n.ui.focus();
19213         if(this.tree.hlDrop){
19214             n.ui.highlight();
19215         }
19216         t.ui.endDrop();
19217         this.tree.fireEvent("nodedrop", de);
19218     },
19219     
19220     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
19221         if(this.tree.hlDrop){
19222             dropNode.ui.focus();
19223             dropNode.ui.highlight();
19224         }
19225         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
19226     },
19227     
19228     getTree : function(){
19229         return this.tree;
19230     },
19231     
19232     removeDropIndicators : function(n){
19233         if(n && n.ddel){
19234             var el = n.ddel;
19235             Roo.fly(el).removeClass([
19236                     "x-tree-drag-insert-above",
19237                     "x-tree-drag-insert-below",
19238                     "x-tree-drag-append"]);
19239             this.lastInsertClass = "_noclass";
19240         }
19241     },
19242     
19243     beforeDragDrop : function(target, e, id){
19244         this.cancelExpand();
19245         return true;
19246     },
19247     
19248     afterRepair : function(data){
19249         if(data && Roo.enableFx){
19250             data.node.ui.highlight();
19251         }
19252         this.hideProxy();
19253     } 
19254     
19255 });
19256
19257 }
19258 /*
19259  * Based on:
19260  * Ext JS Library 1.1.1
19261  * Copyright(c) 2006-2007, Ext JS, LLC.
19262  *
19263  * Originally Released Under LGPL - original licence link has changed is not relivant.
19264  *
19265  * Fork - LGPL
19266  * <script type="text/javascript">
19267  */
19268  
19269
19270 if(Roo.dd.DragZone){
19271 Roo.tree.TreeDragZone = function(tree, config){
19272     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
19273     this.tree = tree;
19274 };
19275
19276 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
19277     ddGroup : "TreeDD",
19278    
19279     onBeforeDrag : function(data, e){
19280         var n = data.node;
19281         return n && n.draggable && !n.disabled;
19282     },
19283      
19284     
19285     onInitDrag : function(e){
19286         var data = this.dragData;
19287         this.tree.getSelectionModel().select(data.node);
19288         this.proxy.update("");
19289         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
19290         this.tree.fireEvent("startdrag", this.tree, data.node, e);
19291     },
19292     
19293     getRepairXY : function(e, data){
19294         return data.node.ui.getDDRepairXY();
19295     },
19296     
19297     onEndDrag : function(data, e){
19298         this.tree.fireEvent("enddrag", this.tree, data.node, e);
19299         
19300         
19301     },
19302     
19303     onValidDrop : function(dd, e, id){
19304         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
19305         this.hideProxy();
19306     },
19307     
19308     beforeInvalidDrop : function(e, id){
19309         // this scrolls the original position back into view
19310         var sm = this.tree.getSelectionModel();
19311         sm.clearSelections();
19312         sm.select(this.dragData.node);
19313     }
19314 });
19315 }/*
19316  * Based on:
19317  * Ext JS Library 1.1.1
19318  * Copyright(c) 2006-2007, Ext JS, LLC.
19319  *
19320  * Originally Released Under LGPL - original licence link has changed is not relivant.
19321  *
19322  * Fork - LGPL
19323  * <script type="text/javascript">
19324  */
19325 /**
19326  * @class Roo.tree.TreeEditor
19327  * @extends Roo.Editor
19328  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
19329  * as the editor field.
19330  * @constructor
19331  * @param {Object} config (used to be the tree panel.)
19332  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
19333  * 
19334  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
19335  * @cfg {Roo.form.TextField|Object} field The field configuration
19336  *
19337  * 
19338  */
19339 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
19340     var tree = config;
19341     var field;
19342     if (oldconfig) { // old style..
19343         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
19344     } else {
19345         // new style..
19346         tree = config.tree;
19347         config.field = config.field  || {};
19348         config.field.xtype = 'TextField';
19349         field = Roo.factory(config.field, Roo.form);
19350     }
19351     config = config || {};
19352     
19353     
19354     this.addEvents({
19355         /**
19356          * @event beforenodeedit
19357          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
19358          * false from the handler of this event.
19359          * @param {Editor} this
19360          * @param {Roo.tree.Node} node 
19361          */
19362         "beforenodeedit" : true
19363     });
19364     
19365     //Roo.log(config);
19366     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
19367
19368     this.tree = tree;
19369
19370     tree.on('beforeclick', this.beforeNodeClick, this);
19371     tree.getTreeEl().on('mousedown', this.hide, this);
19372     this.on('complete', this.updateNode, this);
19373     this.on('beforestartedit', this.fitToTree, this);
19374     this.on('startedit', this.bindScroll, this, {delay:10});
19375     this.on('specialkey', this.onSpecialKey, this);
19376 };
19377
19378 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
19379     /**
19380      * @cfg {String} alignment
19381      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
19382      */
19383     alignment: "l-l",
19384     // inherit
19385     autoSize: false,
19386     /**
19387      * @cfg {Boolean} hideEl
19388      * True to hide the bound element while the editor is displayed (defaults to false)
19389      */
19390     hideEl : false,
19391     /**
19392      * @cfg {String} cls
19393      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
19394      */
19395     cls: "x-small-editor x-tree-editor",
19396     /**
19397      * @cfg {Boolean} shim
19398      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
19399      */
19400     shim:false,
19401     // inherit
19402     shadow:"frame",
19403     /**
19404      * @cfg {Number} maxWidth
19405      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
19406      * the containing tree element's size, it will be automatically limited for you to the container width, taking
19407      * scroll and client offsets into account prior to each edit.
19408      */
19409     maxWidth: 250,
19410
19411     editDelay : 350,
19412
19413     // private
19414     fitToTree : function(ed, el){
19415         var td = this.tree.getTreeEl().dom, nd = el.dom;
19416         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
19417             td.scrollLeft = nd.offsetLeft;
19418         }
19419         var w = Math.min(
19420                 this.maxWidth,
19421                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
19422         this.setSize(w, '');
19423         
19424         return this.fireEvent('beforenodeedit', this, this.editNode);
19425         
19426     },
19427
19428     // private
19429     triggerEdit : function(node){
19430         this.completeEdit();
19431         this.editNode = node;
19432         this.startEdit(node.ui.textNode, node.text);
19433     },
19434
19435     // private
19436     bindScroll : function(){
19437         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
19438     },
19439
19440     // private
19441     beforeNodeClick : function(node, e){
19442         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
19443         this.lastClick = new Date();
19444         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
19445             e.stopEvent();
19446             this.triggerEdit(node);
19447             return false;
19448         }
19449         return true;
19450     },
19451
19452     // private
19453     updateNode : function(ed, value){
19454         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
19455         this.editNode.setText(value);
19456     },
19457
19458     // private
19459     onHide : function(){
19460         Roo.tree.TreeEditor.superclass.onHide.call(this);
19461         if(this.editNode){
19462             this.editNode.ui.focus();
19463         }
19464     },
19465
19466     // private
19467     onSpecialKey : function(field, e){
19468         var k = e.getKey();
19469         if(k == e.ESC){
19470             e.stopEvent();
19471             this.cancelEdit();
19472         }else if(k == e.ENTER && !e.hasModifier()){
19473             e.stopEvent();
19474             this.completeEdit();
19475         }
19476     }
19477 });//<Script type="text/javascript">
19478 /*
19479  * Based on:
19480  * Ext JS Library 1.1.1
19481  * Copyright(c) 2006-2007, Ext JS, LLC.
19482  *
19483  * Originally Released Under LGPL - original licence link has changed is not relivant.
19484  *
19485  * Fork - LGPL
19486  * <script type="text/javascript">
19487  */
19488  
19489 /**
19490  * Not documented??? - probably should be...
19491  */
19492
19493 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
19494     //focus: Roo.emptyFn, // prevent odd scrolling behavior
19495     
19496     renderElements : function(n, a, targetNode, bulkRender){
19497         //consel.log("renderElements?");
19498         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
19499
19500         var t = n.getOwnerTree();
19501         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
19502         
19503         var cols = t.columns;
19504         var bw = t.borderWidth;
19505         var c = cols[0];
19506         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
19507          var cb = typeof a.checked == "boolean";
19508         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19509         var colcls = 'x-t-' + tid + '-c0';
19510         var buf = [
19511             '<li class="x-tree-node">',
19512             
19513                 
19514                 '<div class="x-tree-node-el ', a.cls,'">',
19515                     // extran...
19516                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
19517                 
19518                 
19519                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
19520                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
19521                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
19522                            (a.icon ? ' x-tree-node-inline-icon' : ''),
19523                            (a.iconCls ? ' '+a.iconCls : ''),
19524                            '" unselectable="on" />',
19525                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
19526                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
19527                              
19528                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19529                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
19530                             '<span unselectable="on" qtip="' + tx + '">',
19531                              tx,
19532                              '</span></a>' ,
19533                     '</div>',
19534                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19535                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
19536                  ];
19537         for(var i = 1, len = cols.length; i < len; i++){
19538             c = cols[i];
19539             colcls = 'x-t-' + tid + '-c' +i;
19540             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19541             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
19542                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
19543                       "</div>");
19544          }
19545          
19546          buf.push(
19547             '</a>',
19548             '<div class="x-clear"></div></div>',
19549             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
19550             "</li>");
19551         
19552         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
19553             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
19554                                 n.nextSibling.ui.getEl(), buf.join(""));
19555         }else{
19556             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
19557         }
19558         var el = this.wrap.firstChild;
19559         this.elRow = el;
19560         this.elNode = el.firstChild;
19561         this.ranchor = el.childNodes[1];
19562         this.ctNode = this.wrap.childNodes[1];
19563         var cs = el.firstChild.childNodes;
19564         this.indentNode = cs[0];
19565         this.ecNode = cs[1];
19566         this.iconNode = cs[2];
19567         var index = 3;
19568         if(cb){
19569             this.checkbox = cs[3];
19570             index++;
19571         }
19572         this.anchor = cs[index];
19573         
19574         this.textNode = cs[index].firstChild;
19575         
19576         //el.on("click", this.onClick, this);
19577         //el.on("dblclick", this.onDblClick, this);
19578         
19579         
19580        // console.log(this);
19581     },
19582     initEvents : function(){
19583         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
19584         
19585             
19586         var a = this.ranchor;
19587
19588         var el = Roo.get(a);
19589
19590         if(Roo.isOpera){ // opera render bug ignores the CSS
19591             el.setStyle("text-decoration", "none");
19592         }
19593
19594         el.on("click", this.onClick, this);
19595         el.on("dblclick", this.onDblClick, this);
19596         el.on("contextmenu", this.onContextMenu, this);
19597         
19598     },
19599     
19600     /*onSelectedChange : function(state){
19601         if(state){
19602             this.focus();
19603             this.addClass("x-tree-selected");
19604         }else{
19605             //this.blur();
19606             this.removeClass("x-tree-selected");
19607         }
19608     },*/
19609     addClass : function(cls){
19610         if(this.elRow){
19611             Roo.fly(this.elRow).addClass(cls);
19612         }
19613         
19614     },
19615     
19616     
19617     removeClass : function(cls){
19618         if(this.elRow){
19619             Roo.fly(this.elRow).removeClass(cls);
19620         }
19621     }
19622
19623     
19624     
19625 });//<Script type="text/javascript">
19626
19627 /*
19628  * Based on:
19629  * Ext JS Library 1.1.1
19630  * Copyright(c) 2006-2007, Ext JS, LLC.
19631  *
19632  * Originally Released Under LGPL - original licence link has changed is not relivant.
19633  *
19634  * Fork - LGPL
19635  * <script type="text/javascript">
19636  */
19637  
19638
19639 /**
19640  * @class Roo.tree.ColumnTree
19641  * @extends Roo.data.TreePanel
19642  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
19643  * @cfg {int} borderWidth  compined right/left border allowance
19644  * @constructor
19645  * @param {String/HTMLElement/Element} el The container element
19646  * @param {Object} config
19647  */
19648 Roo.tree.ColumnTree =  function(el, config)
19649 {
19650    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
19651    this.addEvents({
19652         /**
19653         * @event resize
19654         * Fire this event on a container when it resizes
19655         * @param {int} w Width
19656         * @param {int} h Height
19657         */
19658        "resize" : true
19659     });
19660     this.on('resize', this.onResize, this);
19661 };
19662
19663 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
19664     //lines:false,
19665     
19666     
19667     borderWidth: Roo.isBorderBox ? 0 : 2, 
19668     headEls : false,
19669     
19670     render : function(){
19671         // add the header.....
19672        
19673         Roo.tree.ColumnTree.superclass.render.apply(this);
19674         
19675         this.el.addClass('x-column-tree');
19676         
19677         this.headers = this.el.createChild(
19678             {cls:'x-tree-headers'},this.innerCt.dom);
19679    
19680         var cols = this.columns, c;
19681         var totalWidth = 0;
19682         this.headEls = [];
19683         var  len = cols.length;
19684         for(var i = 0; i < len; i++){
19685              c = cols[i];
19686              totalWidth += c.width;
19687             this.headEls.push(this.headers.createChild({
19688                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19689                  cn: {
19690                      cls:'x-tree-hd-text',
19691                      html: c.header
19692                  },
19693                  style:'width:'+(c.width-this.borderWidth)+'px;'
19694              }));
19695         }
19696         this.headers.createChild({cls:'x-clear'});
19697         // prevent floats from wrapping when clipped
19698         this.headers.setWidth(totalWidth);
19699         //this.innerCt.setWidth(totalWidth);
19700         this.innerCt.setStyle({ overflow: 'auto' });
19701         this.onResize(this.width, this.height);
19702              
19703         
19704     },
19705     onResize : function(w,h)
19706     {
19707         this.height = h;
19708         this.width = w;
19709         // resize cols..
19710         this.innerCt.setWidth(this.width);
19711         this.innerCt.setHeight(this.height-20);
19712         
19713         // headers...
19714         var cols = this.columns, c;
19715         var totalWidth = 0;
19716         var expEl = false;
19717         var len = cols.length;
19718         for(var i = 0; i < len; i++){
19719             c = cols[i];
19720             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19721                 // it's the expander..
19722                 expEl  = this.headEls[i];
19723                 continue;
19724             }
19725             totalWidth += c.width;
19726             
19727         }
19728         if (expEl) {
19729             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
19730         }
19731         this.headers.setWidth(w-20);
19732
19733         
19734         
19735         
19736     }
19737 });
19738 /*
19739  * Based on:
19740  * Ext JS Library 1.1.1
19741  * Copyright(c) 2006-2007, Ext JS, LLC.
19742  *
19743  * Originally Released Under LGPL - original licence link has changed is not relivant.
19744  *
19745  * Fork - LGPL
19746  * <script type="text/javascript">
19747  */
19748  
19749 /**
19750  * @class Roo.menu.Menu
19751  * @extends Roo.util.Observable
19752  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
19753  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19754  * @constructor
19755  * Creates a new Menu
19756  * @param {Object} config Configuration options
19757  */
19758 Roo.menu.Menu = function(config){
19759     Roo.apply(this, config);
19760     this.id = this.id || Roo.id();
19761     this.addEvents({
19762         /**
19763          * @event beforeshow
19764          * Fires before this menu is displayed
19765          * @param {Roo.menu.Menu} this
19766          */
19767         beforeshow : true,
19768         /**
19769          * @event beforehide
19770          * Fires before this menu is hidden
19771          * @param {Roo.menu.Menu} this
19772          */
19773         beforehide : true,
19774         /**
19775          * @event show
19776          * Fires after this menu is displayed
19777          * @param {Roo.menu.Menu} this
19778          */
19779         show : true,
19780         /**
19781          * @event hide
19782          * Fires after this menu is hidden
19783          * @param {Roo.menu.Menu} this
19784          */
19785         hide : true,
19786         /**
19787          * @event click
19788          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19789          * @param {Roo.menu.Menu} this
19790          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19791          * @param {Roo.EventObject} e
19792          */
19793         click : true,
19794         /**
19795          * @event mouseover
19796          * Fires when the mouse is hovering over this menu
19797          * @param {Roo.menu.Menu} this
19798          * @param {Roo.EventObject} e
19799          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19800          */
19801         mouseover : true,
19802         /**
19803          * @event mouseout
19804          * Fires when the mouse exits this menu
19805          * @param {Roo.menu.Menu} this
19806          * @param {Roo.EventObject} e
19807          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19808          */
19809         mouseout : true,
19810         /**
19811          * @event itemclick
19812          * Fires when a menu item contained in this menu is clicked
19813          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19814          * @param {Roo.EventObject} e
19815          */
19816         itemclick: true
19817     });
19818     if (this.registerMenu) {
19819         Roo.menu.MenuMgr.register(this);
19820     }
19821     
19822     var mis = this.items;
19823     this.items = new Roo.util.MixedCollection();
19824     if(mis){
19825         this.add.apply(this, mis);
19826     }
19827 };
19828
19829 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19830     /**
19831      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19832      */
19833     minWidth : 120,
19834     /**
19835      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19836      * for bottom-right shadow (defaults to "sides")
19837      */
19838     shadow : "sides",
19839     /**
19840      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19841      * this menu (defaults to "tl-tr?")
19842      */
19843     subMenuAlign : "tl-tr?",
19844     /**
19845      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19846      * relative to its element of origin (defaults to "tl-bl?")
19847      */
19848     defaultAlign : "tl-bl?",
19849     /**
19850      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19851      */
19852     allowOtherMenus : false,
19853     /**
19854      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19855      */
19856     registerMenu : true,
19857
19858     hidden:true,
19859
19860     // private
19861     render : function(){
19862         if(this.el){
19863             return;
19864         }
19865         var el = this.el = new Roo.Layer({
19866             cls: "x-menu",
19867             shadow:this.shadow,
19868             constrain: false,
19869             parentEl: this.parentEl || document.body,
19870             zindex:15000
19871         });
19872
19873         this.keyNav = new Roo.menu.MenuNav(this);
19874
19875         if(this.plain){
19876             el.addClass("x-menu-plain");
19877         }
19878         if(this.cls){
19879             el.addClass(this.cls);
19880         }
19881         // generic focus element
19882         this.focusEl = el.createChild({
19883             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19884         });
19885         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19886         ul.on("click", this.onClick, this);
19887         ul.on("mouseover", this.onMouseOver, this);
19888         ul.on("mouseout", this.onMouseOut, this);
19889         this.items.each(function(item){
19890             if (item.hidden) {
19891                 return;
19892             }
19893             
19894             var li = document.createElement("li");
19895             li.className = "x-menu-list-item";
19896             ul.dom.appendChild(li);
19897             item.render(li, this);
19898         }, this);
19899         this.ul = ul;
19900         this.autoWidth();
19901     },
19902
19903     // private
19904     autoWidth : function(){
19905         var el = this.el, ul = this.ul;
19906         if(!el){
19907             return;
19908         }
19909         var w = this.width;
19910         if(w){
19911             el.setWidth(w);
19912         }else if(Roo.isIE){
19913             el.setWidth(this.minWidth);
19914             var t = el.dom.offsetWidth; // force recalc
19915             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19916         }
19917     },
19918
19919     // private
19920     delayAutoWidth : function(){
19921         if(this.rendered){
19922             if(!this.awTask){
19923                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19924             }
19925             this.awTask.delay(20);
19926         }
19927     },
19928
19929     // private
19930     findTargetItem : function(e){
19931         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
19932         if(t && t.menuItemId){
19933             return this.items.get(t.menuItemId);
19934         }
19935     },
19936
19937     // private
19938     onClick : function(e){
19939         var t;
19940         if(t = this.findTargetItem(e)){
19941             t.onClick(e);
19942             this.fireEvent("click", this, t, e);
19943         }
19944     },
19945
19946     // private
19947     setActiveItem : function(item, autoExpand){
19948         if(item != this.activeItem){
19949             if(this.activeItem){
19950                 this.activeItem.deactivate();
19951             }
19952             this.activeItem = item;
19953             item.activate(autoExpand);
19954         }else if(autoExpand){
19955             item.expandMenu();
19956         }
19957     },
19958
19959     // private
19960     tryActivate : function(start, step){
19961         var items = this.items;
19962         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19963             var item = items.get(i);
19964             if(!item.disabled && item.canActivate){
19965                 this.setActiveItem(item, false);
19966                 return item;
19967             }
19968         }
19969         return false;
19970     },
19971
19972     // private
19973     onMouseOver : function(e){
19974         var t;
19975         if(t = this.findTargetItem(e)){
19976             if(t.canActivate && !t.disabled){
19977                 this.setActiveItem(t, true);
19978             }
19979         }
19980         this.fireEvent("mouseover", this, e, t);
19981     },
19982
19983     // private
19984     onMouseOut : function(e){
19985         var t;
19986         if(t = this.findTargetItem(e)){
19987             if(t == this.activeItem && t.shouldDeactivate(e)){
19988                 this.activeItem.deactivate();
19989                 delete this.activeItem;
19990             }
19991         }
19992         this.fireEvent("mouseout", this, e, t);
19993     },
19994
19995     /**
19996      * Read-only.  Returns true if the menu is currently displayed, else false.
19997      * @type Boolean
19998      */
19999     isVisible : function(){
20000         return this.el && !this.hidden;
20001     },
20002
20003     /**
20004      * Displays this menu relative to another element
20005      * @param {String/HTMLElement/Roo.Element} element The element to align to
20006      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
20007      * the element (defaults to this.defaultAlign)
20008      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
20009      */
20010     show : function(el, pos, parentMenu){
20011         this.parentMenu = parentMenu;
20012         if(!this.el){
20013             this.render();
20014         }
20015         this.fireEvent("beforeshow", this);
20016         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
20017     },
20018
20019     /**
20020      * Displays this menu at a specific xy position
20021      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
20022      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
20023      */
20024     showAt : function(xy, parentMenu, /* private: */_e){
20025         this.parentMenu = parentMenu;
20026         if(!this.el){
20027             this.render();
20028         }
20029         if(_e !== false){
20030             this.fireEvent("beforeshow", this);
20031             xy = this.el.adjustForConstraints(xy);
20032         }
20033         this.el.setXY(xy);
20034         this.el.show();
20035         this.hidden = false;
20036         this.focus();
20037         this.fireEvent("show", this);
20038     },
20039
20040     focus : function(){
20041         if(!this.hidden){
20042             this.doFocus.defer(50, this);
20043         }
20044     },
20045
20046     doFocus : function(){
20047         if(!this.hidden){
20048             this.focusEl.focus();
20049         }
20050     },
20051
20052     /**
20053      * Hides this menu and optionally all parent menus
20054      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
20055      */
20056     hide : function(deep){
20057         if(this.el && this.isVisible()){
20058             this.fireEvent("beforehide", this);
20059             if(this.activeItem){
20060                 this.activeItem.deactivate();
20061                 this.activeItem = null;
20062             }
20063             this.el.hide();
20064             this.hidden = true;
20065             this.fireEvent("hide", this);
20066         }
20067         if(deep === true && this.parentMenu){
20068             this.parentMenu.hide(true);
20069         }
20070     },
20071
20072     /**
20073      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
20074      * Any of the following are valid:
20075      * <ul>
20076      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
20077      * <li>An HTMLElement object which will be converted to a menu item</li>
20078      * <li>A menu item config object that will be created as a new menu item</li>
20079      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
20080      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
20081      * </ul>
20082      * Usage:
20083      * <pre><code>
20084 // Create the menu
20085 var menu = new Roo.menu.Menu();
20086
20087 // Create a menu item to add by reference
20088 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
20089
20090 // Add a bunch of items at once using different methods.
20091 // Only the last item added will be returned.
20092 var item = menu.add(
20093     menuItem,                // add existing item by ref
20094     'Dynamic Item',          // new TextItem
20095     '-',                     // new separator
20096     { text: 'Config Item' }  // new item by config
20097 );
20098 </code></pre>
20099      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
20100      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
20101      */
20102     add : function(){
20103         var a = arguments, l = a.length, item;
20104         for(var i = 0; i < l; i++){
20105             var el = a[i];
20106             if ((typeof(el) == "object") && el.xtype && el.xns) {
20107                 el = Roo.factory(el, Roo.menu);
20108             }
20109             
20110             if(el.render){ // some kind of Item
20111                 item = this.addItem(el);
20112             }else if(typeof el == "string"){ // string
20113                 if(el == "separator" || el == "-"){
20114                     item = this.addSeparator();
20115                 }else{
20116                     item = this.addText(el);
20117                 }
20118             }else if(el.tagName || el.el){ // element
20119                 item = this.addElement(el);
20120             }else if(typeof el == "object"){ // must be menu item config?
20121                 item = this.addMenuItem(el);
20122             }
20123         }
20124         return item;
20125     },
20126
20127     /**
20128      * Returns this menu's underlying {@link Roo.Element} object
20129      * @return {Roo.Element} The element
20130      */
20131     getEl : function(){
20132         if(!this.el){
20133             this.render();
20134         }
20135         return this.el;
20136     },
20137
20138     /**
20139      * Adds a separator bar to the menu
20140      * @return {Roo.menu.Item} The menu item that was added
20141      */
20142     addSeparator : function(){
20143         return this.addItem(new Roo.menu.Separator());
20144     },
20145
20146     /**
20147      * Adds an {@link Roo.Element} object to the menu
20148      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
20149      * @return {Roo.menu.Item} The menu item that was added
20150      */
20151     addElement : function(el){
20152         return this.addItem(new Roo.menu.BaseItem(el));
20153     },
20154
20155     /**
20156      * Adds an existing object based on {@link Roo.menu.Item} to the menu
20157      * @param {Roo.menu.Item} item The menu item to add
20158      * @return {Roo.menu.Item} The menu item that was added
20159      */
20160     addItem : function(item){
20161         this.items.add(item);
20162         if(this.ul){
20163             var li = document.createElement("li");
20164             li.className = "x-menu-list-item";
20165             this.ul.dom.appendChild(li);
20166             item.render(li, this);
20167             this.delayAutoWidth();
20168         }
20169         return item;
20170     },
20171
20172     /**
20173      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
20174      * @param {Object} config A MenuItem config object
20175      * @return {Roo.menu.Item} The menu item that was added
20176      */
20177     addMenuItem : function(config){
20178         if(!(config instanceof Roo.menu.Item)){
20179             if(typeof config.checked == "boolean"){ // must be check menu item config?
20180                 config = new Roo.menu.CheckItem(config);
20181             }else{
20182                 config = new Roo.menu.Item(config);
20183             }
20184         }
20185         return this.addItem(config);
20186     },
20187
20188     /**
20189      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
20190      * @param {String} text The text to display in the menu item
20191      * @return {Roo.menu.Item} The menu item that was added
20192      */
20193     addText : function(text){
20194         return this.addItem(new Roo.menu.TextItem({ text : text }));
20195     },
20196
20197     /**
20198      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
20199      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
20200      * @param {Roo.menu.Item} item The menu item to add
20201      * @return {Roo.menu.Item} The menu item that was added
20202      */
20203     insert : function(index, item){
20204         this.items.insert(index, item);
20205         if(this.ul){
20206             var li = document.createElement("li");
20207             li.className = "x-menu-list-item";
20208             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
20209             item.render(li, this);
20210             this.delayAutoWidth();
20211         }
20212         return item;
20213     },
20214
20215     /**
20216      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
20217      * @param {Roo.menu.Item} item The menu item to remove
20218      */
20219     remove : function(item){
20220         this.items.removeKey(item.id);
20221         item.destroy();
20222     },
20223
20224     /**
20225      * Removes and destroys all items in the menu
20226      */
20227     removeAll : function(){
20228         var f;
20229         while(f = this.items.first()){
20230             this.remove(f);
20231         }
20232     }
20233 });
20234
20235 // MenuNav is a private utility class used internally by the Menu
20236 Roo.menu.MenuNav = function(menu){
20237     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
20238     this.scope = this.menu = menu;
20239 };
20240
20241 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
20242     doRelay : function(e, h){
20243         var k = e.getKey();
20244         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
20245             this.menu.tryActivate(0, 1);
20246             return false;
20247         }
20248         return h.call(this.scope || this, e, this.menu);
20249     },
20250
20251     up : function(e, m){
20252         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
20253             m.tryActivate(m.items.length-1, -1);
20254         }
20255     },
20256
20257     down : function(e, m){
20258         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
20259             m.tryActivate(0, 1);
20260         }
20261     },
20262
20263     right : function(e, m){
20264         if(m.activeItem){
20265             m.activeItem.expandMenu(true);
20266         }
20267     },
20268
20269     left : function(e, m){
20270         m.hide();
20271         if(m.parentMenu && m.parentMenu.activeItem){
20272             m.parentMenu.activeItem.activate();
20273         }
20274     },
20275
20276     enter : function(e, m){
20277         if(m.activeItem){
20278             e.stopPropagation();
20279             m.activeItem.onClick(e);
20280             m.fireEvent("click", this, m.activeItem);
20281             return true;
20282         }
20283     }
20284 });/*
20285  * Based on:
20286  * Ext JS Library 1.1.1
20287  * Copyright(c) 2006-2007, Ext JS, LLC.
20288  *
20289  * Originally Released Under LGPL - original licence link has changed is not relivant.
20290  *
20291  * Fork - LGPL
20292  * <script type="text/javascript">
20293  */
20294  
20295 /**
20296  * @class Roo.menu.MenuMgr
20297  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
20298  * @singleton
20299  */
20300 Roo.menu.MenuMgr = function(){
20301    var menus, active, groups = {}, attached = false, lastShow = new Date();
20302
20303    // private - called when first menu is created
20304    function init(){
20305        menus = {};
20306        active = new Roo.util.MixedCollection();
20307        Roo.get(document).addKeyListener(27, function(){
20308            if(active.length > 0){
20309                hideAll();
20310            }
20311        });
20312    }
20313
20314    // private
20315    function hideAll(){
20316        if(active && active.length > 0){
20317            var c = active.clone();
20318            c.each(function(m){
20319                m.hide();
20320            });
20321        }
20322    }
20323
20324    // private
20325    function onHide(m){
20326        active.remove(m);
20327        if(active.length < 1){
20328            Roo.get(document).un("mousedown", onMouseDown);
20329            attached = false;
20330        }
20331    }
20332
20333    // private
20334    function onShow(m){
20335        var last = active.last();
20336        lastShow = new Date();
20337        active.add(m);
20338        if(!attached){
20339            Roo.get(document).on("mousedown", onMouseDown);
20340            attached = true;
20341        }
20342        if(m.parentMenu){
20343           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
20344           m.parentMenu.activeChild = m;
20345        }else if(last && last.isVisible()){
20346           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
20347        }
20348    }
20349
20350    // private
20351    function onBeforeHide(m){
20352        if(m.activeChild){
20353            m.activeChild.hide();
20354        }
20355        if(m.autoHideTimer){
20356            clearTimeout(m.autoHideTimer);
20357            delete m.autoHideTimer;
20358        }
20359    }
20360
20361    // private
20362    function onBeforeShow(m){
20363        var pm = m.parentMenu;
20364        if(!pm && !m.allowOtherMenus){
20365            hideAll();
20366        }else if(pm && pm.activeChild && active != m){
20367            pm.activeChild.hide();
20368        }
20369    }
20370
20371    // private
20372    function onMouseDown(e){
20373        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
20374            hideAll();
20375        }
20376    }
20377
20378    // private
20379    function onBeforeCheck(mi, state){
20380        if(state){
20381            var g = groups[mi.group];
20382            for(var i = 0, l = g.length; i < l; i++){
20383                if(g[i] != mi){
20384                    g[i].setChecked(false);
20385                }
20386            }
20387        }
20388    }
20389
20390    return {
20391
20392        /**
20393         * Hides all menus that are currently visible
20394         */
20395        hideAll : function(){
20396             hideAll();  
20397        },
20398
20399        // private
20400        register : function(menu){
20401            if(!menus){
20402                init();
20403            }
20404            menus[menu.id] = menu;
20405            menu.on("beforehide", onBeforeHide);
20406            menu.on("hide", onHide);
20407            menu.on("beforeshow", onBeforeShow);
20408            menu.on("show", onShow);
20409            var g = menu.group;
20410            if(g && menu.events["checkchange"]){
20411                if(!groups[g]){
20412                    groups[g] = [];
20413                }
20414                groups[g].push(menu);
20415                menu.on("checkchange", onCheck);
20416            }
20417        },
20418
20419         /**
20420          * Returns a {@link Roo.menu.Menu} object
20421          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
20422          * be used to generate and return a new Menu instance.
20423          */
20424        get : function(menu){
20425            if(typeof menu == "string"){ // menu id
20426                return menus[menu];
20427            }else if(menu.events){  // menu instance
20428                return menu;
20429            }else if(typeof menu.length == 'number'){ // array of menu items?
20430                return new Roo.menu.Menu({items:menu});
20431            }else{ // otherwise, must be a config
20432                return new Roo.menu.Menu(menu);
20433            }
20434        },
20435
20436        // private
20437        unregister : function(menu){
20438            delete menus[menu.id];
20439            menu.un("beforehide", onBeforeHide);
20440            menu.un("hide", onHide);
20441            menu.un("beforeshow", onBeforeShow);
20442            menu.un("show", onShow);
20443            var g = menu.group;
20444            if(g && menu.events["checkchange"]){
20445                groups[g].remove(menu);
20446                menu.un("checkchange", onCheck);
20447            }
20448        },
20449
20450        // private
20451        registerCheckable : function(menuItem){
20452            var g = menuItem.group;
20453            if(g){
20454                if(!groups[g]){
20455                    groups[g] = [];
20456                }
20457                groups[g].push(menuItem);
20458                menuItem.on("beforecheckchange", onBeforeCheck);
20459            }
20460        },
20461
20462        // private
20463        unregisterCheckable : function(menuItem){
20464            var g = menuItem.group;
20465            if(g){
20466                groups[g].remove(menuItem);
20467                menuItem.un("beforecheckchange", onBeforeCheck);
20468            }
20469        }
20470    };
20471 }();/*
20472  * Based on:
20473  * Ext JS Library 1.1.1
20474  * Copyright(c) 2006-2007, Ext JS, LLC.
20475  *
20476  * Originally Released Under LGPL - original licence link has changed is not relivant.
20477  *
20478  * Fork - LGPL
20479  * <script type="text/javascript">
20480  */
20481  
20482
20483 /**
20484  * @class Roo.menu.BaseItem
20485  * @extends Roo.Component
20486  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
20487  * management and base configuration options shared by all menu components.
20488  * @constructor
20489  * Creates a new BaseItem
20490  * @param {Object} config Configuration options
20491  */
20492 Roo.menu.BaseItem = function(config){
20493     Roo.menu.BaseItem.superclass.constructor.call(this, config);
20494
20495     this.addEvents({
20496         /**
20497          * @event click
20498          * Fires when this item is clicked
20499          * @param {Roo.menu.BaseItem} this
20500          * @param {Roo.EventObject} e
20501          */
20502         click: true,
20503         /**
20504          * @event activate
20505          * Fires when this item is activated
20506          * @param {Roo.menu.BaseItem} this
20507          */
20508         activate : true,
20509         /**
20510          * @event deactivate
20511          * Fires when this item is deactivated
20512          * @param {Roo.menu.BaseItem} this
20513          */
20514         deactivate : true
20515     });
20516
20517     if(this.handler){
20518         this.on("click", this.handler, this.scope, true);
20519     }
20520 };
20521
20522 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
20523     /**
20524      * @cfg {Function} handler
20525      * A function that will handle the click event of this menu item (defaults to undefined)
20526      */
20527     /**
20528      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
20529      */
20530     canActivate : false,
20531     
20532      /**
20533      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
20534      */
20535     hidden: false,
20536     
20537     /**
20538      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
20539      */
20540     activeClass : "x-menu-item-active",
20541     /**
20542      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
20543      */
20544     hideOnClick : true,
20545     /**
20546      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
20547      */
20548     hideDelay : 100,
20549
20550     // private
20551     ctype: "Roo.menu.BaseItem",
20552
20553     // private
20554     actionMode : "container",
20555
20556     // private
20557     render : function(container, parentMenu){
20558         this.parentMenu = parentMenu;
20559         Roo.menu.BaseItem.superclass.render.call(this, container);
20560         this.container.menuItemId = this.id;
20561     },
20562
20563     // private
20564     onRender : function(container, position){
20565         this.el = Roo.get(this.el);
20566         container.dom.appendChild(this.el.dom);
20567     },
20568
20569     // private
20570     onClick : function(e){
20571         if(!this.disabled && this.fireEvent("click", this, e) !== false
20572                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
20573             this.handleClick(e);
20574         }else{
20575             e.stopEvent();
20576         }
20577     },
20578
20579     // private
20580     activate : function(){
20581         if(this.disabled){
20582             return false;
20583         }
20584         var li = this.container;
20585         li.addClass(this.activeClass);
20586         this.region = li.getRegion().adjust(2, 2, -2, -2);
20587         this.fireEvent("activate", this);
20588         return true;
20589     },
20590
20591     // private
20592     deactivate : function(){
20593         this.container.removeClass(this.activeClass);
20594         this.fireEvent("deactivate", this);
20595     },
20596
20597     // private
20598     shouldDeactivate : function(e){
20599         return !this.region || !this.region.contains(e.getPoint());
20600     },
20601
20602     // private
20603     handleClick : function(e){
20604         if(this.hideOnClick){
20605             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
20606         }
20607     },
20608
20609     // private
20610     expandMenu : function(autoActivate){
20611         // do nothing
20612     },
20613
20614     // private
20615     hideMenu : function(){
20616         // do nothing
20617     }
20618 });/*
20619  * Based on:
20620  * Ext JS Library 1.1.1
20621  * Copyright(c) 2006-2007, Ext JS, LLC.
20622  *
20623  * Originally Released Under LGPL - original licence link has changed is not relivant.
20624  *
20625  * Fork - LGPL
20626  * <script type="text/javascript">
20627  */
20628  
20629 /**
20630  * @class Roo.menu.Adapter
20631  * @extends Roo.menu.BaseItem
20632  * 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.
20633  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
20634  * @constructor
20635  * Creates a new Adapter
20636  * @param {Object} config Configuration options
20637  */
20638 Roo.menu.Adapter = function(component, config){
20639     Roo.menu.Adapter.superclass.constructor.call(this, config);
20640     this.component = component;
20641 };
20642 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
20643     // private
20644     canActivate : true,
20645
20646     // private
20647     onRender : function(container, position){
20648         this.component.render(container);
20649         this.el = this.component.getEl();
20650     },
20651
20652     // private
20653     activate : function(){
20654         if(this.disabled){
20655             return false;
20656         }
20657         this.component.focus();
20658         this.fireEvent("activate", this);
20659         return true;
20660     },
20661
20662     // private
20663     deactivate : function(){
20664         this.fireEvent("deactivate", this);
20665     },
20666
20667     // private
20668     disable : function(){
20669         this.component.disable();
20670         Roo.menu.Adapter.superclass.disable.call(this);
20671     },
20672
20673     // private
20674     enable : function(){
20675         this.component.enable();
20676         Roo.menu.Adapter.superclass.enable.call(this);
20677     }
20678 });/*
20679  * Based on:
20680  * Ext JS Library 1.1.1
20681  * Copyright(c) 2006-2007, Ext JS, LLC.
20682  *
20683  * Originally Released Under LGPL - original licence link has changed is not relivant.
20684  *
20685  * Fork - LGPL
20686  * <script type="text/javascript">
20687  */
20688
20689 /**
20690  * @class Roo.menu.TextItem
20691  * @extends Roo.menu.BaseItem
20692  * Adds a static text string to a menu, usually used as either a heading or group separator.
20693  * Note: old style constructor with text is still supported.
20694  * 
20695  * @constructor
20696  * Creates a new TextItem
20697  * @param {Object} cfg Configuration
20698  */
20699 Roo.menu.TextItem = function(cfg){
20700     if (typeof(cfg) == 'string') {
20701         this.text = cfg;
20702     } else {
20703         Roo.apply(this,cfg);
20704     }
20705     
20706     Roo.menu.TextItem.superclass.constructor.call(this);
20707 };
20708
20709 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20710     /**
20711      * @cfg {Boolean} text Text to show on item.
20712      */
20713     text : '',
20714     
20715     /**
20716      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20717      */
20718     hideOnClick : false,
20719     /**
20720      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20721      */
20722     itemCls : "x-menu-text",
20723
20724     // private
20725     onRender : function(){
20726         var s = document.createElement("span");
20727         s.className = this.itemCls;
20728         s.innerHTML = this.text;
20729         this.el = s;
20730         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20731     }
20732 });/*
20733  * Based on:
20734  * Ext JS Library 1.1.1
20735  * Copyright(c) 2006-2007, Ext JS, LLC.
20736  *
20737  * Originally Released Under LGPL - original licence link has changed is not relivant.
20738  *
20739  * Fork - LGPL
20740  * <script type="text/javascript">
20741  */
20742
20743 /**
20744  * @class Roo.menu.Separator
20745  * @extends Roo.menu.BaseItem
20746  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20747  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20748  * @constructor
20749  * @param {Object} config Configuration options
20750  */
20751 Roo.menu.Separator = function(config){
20752     Roo.menu.Separator.superclass.constructor.call(this, config);
20753 };
20754
20755 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20756     /**
20757      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20758      */
20759     itemCls : "x-menu-sep",
20760     /**
20761      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20762      */
20763     hideOnClick : false,
20764
20765     // private
20766     onRender : function(li){
20767         var s = document.createElement("span");
20768         s.className = this.itemCls;
20769         s.innerHTML = "&#160;";
20770         this.el = s;
20771         li.addClass("x-menu-sep-li");
20772         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20773     }
20774 });/*
20775  * Based on:
20776  * Ext JS Library 1.1.1
20777  * Copyright(c) 2006-2007, Ext JS, LLC.
20778  *
20779  * Originally Released Under LGPL - original licence link has changed is not relivant.
20780  *
20781  * Fork - LGPL
20782  * <script type="text/javascript">
20783  */
20784 /**
20785  * @class Roo.menu.Item
20786  * @extends Roo.menu.BaseItem
20787  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20788  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20789  * activation and click handling.
20790  * @constructor
20791  * Creates a new Item
20792  * @param {Object} config Configuration options
20793  */
20794 Roo.menu.Item = function(config){
20795     Roo.menu.Item.superclass.constructor.call(this, config);
20796     if(this.menu){
20797         this.menu = Roo.menu.MenuMgr.get(this.menu);
20798     }
20799 };
20800 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20801     
20802     /**
20803      * @cfg {String} text
20804      * The text to show on the menu item.
20805      */
20806     text: '',
20807      /**
20808      * @cfg {String} HTML to render in menu
20809      * The text to show on the menu item (HTML version).
20810      */
20811     html: '',
20812     /**
20813      * @cfg {String} icon
20814      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20815      */
20816     icon: undefined,
20817     /**
20818      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20819      */
20820     itemCls : "x-menu-item",
20821     /**
20822      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20823      */
20824     canActivate : true,
20825     /**
20826      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20827      */
20828     showDelay: 200,
20829     // doc'd in BaseItem
20830     hideDelay: 200,
20831
20832     // private
20833     ctype: "Roo.menu.Item",
20834     
20835     // private
20836     onRender : function(container, position){
20837         var el = document.createElement("a");
20838         el.hideFocus = true;
20839         el.unselectable = "on";
20840         el.href = this.href || "#";
20841         if(this.hrefTarget){
20842             el.target = this.hrefTarget;
20843         }
20844         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
20845         
20846         var html = this.html.length ? this.html  : String.format('{0}',this.text);
20847         
20848         el.innerHTML = String.format(
20849                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
20850                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
20851         this.el = el;
20852         Roo.menu.Item.superclass.onRender.call(this, container, position);
20853     },
20854
20855     /**
20856      * Sets the text to display in this menu item
20857      * @param {String} text The text to display
20858      * @param {Boolean} isHTML true to indicate text is pure html.
20859      */
20860     setText : function(text, isHTML){
20861         if (isHTML) {
20862             this.html = text;
20863         } else {
20864             this.text = text;
20865             this.html = '';
20866         }
20867         if(this.rendered){
20868             var html = this.html.length ? this.html  : String.format('{0}',this.text);
20869      
20870             this.el.update(String.format(
20871                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
20872                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20873             this.parentMenu.autoWidth();
20874         }
20875     },
20876
20877     // private
20878     handleClick : function(e){
20879         if(!this.href){ // if no link defined, stop the event automatically
20880             e.stopEvent();
20881         }
20882         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20883     },
20884
20885     // private
20886     activate : function(autoExpand){
20887         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20888             this.focus();
20889             if(autoExpand){
20890                 this.expandMenu();
20891             }
20892         }
20893         return true;
20894     },
20895
20896     // private
20897     shouldDeactivate : function(e){
20898         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20899             if(this.menu && this.menu.isVisible()){
20900                 return !this.menu.getEl().getRegion().contains(e.getPoint());
20901             }
20902             return true;
20903         }
20904         return false;
20905     },
20906
20907     // private
20908     deactivate : function(){
20909         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20910         this.hideMenu();
20911     },
20912
20913     // private
20914     expandMenu : function(autoActivate){
20915         if(!this.disabled && this.menu){
20916             clearTimeout(this.hideTimer);
20917             delete this.hideTimer;
20918             if(!this.menu.isVisible() && !this.showTimer){
20919                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20920             }else if (this.menu.isVisible() && autoActivate){
20921                 this.menu.tryActivate(0, 1);
20922             }
20923         }
20924     },
20925
20926     // private
20927     deferExpand : function(autoActivate){
20928         delete this.showTimer;
20929         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20930         if(autoActivate){
20931             this.menu.tryActivate(0, 1);
20932         }
20933     },
20934
20935     // private
20936     hideMenu : function(){
20937         clearTimeout(this.showTimer);
20938         delete this.showTimer;
20939         if(!this.hideTimer && this.menu && this.menu.isVisible()){
20940             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20941         }
20942     },
20943
20944     // private
20945     deferHide : function(){
20946         delete this.hideTimer;
20947         this.menu.hide();
20948     }
20949 });/*
20950  * Based on:
20951  * Ext JS Library 1.1.1
20952  * Copyright(c) 2006-2007, Ext JS, LLC.
20953  *
20954  * Originally Released Under LGPL - original licence link has changed is not relivant.
20955  *
20956  * Fork - LGPL
20957  * <script type="text/javascript">
20958  */
20959  
20960 /**
20961  * @class Roo.menu.CheckItem
20962  * @extends Roo.menu.Item
20963  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20964  * @constructor
20965  * Creates a new CheckItem
20966  * @param {Object} config Configuration options
20967  */
20968 Roo.menu.CheckItem = function(config){
20969     Roo.menu.CheckItem.superclass.constructor.call(this, config);
20970     this.addEvents({
20971         /**
20972          * @event beforecheckchange
20973          * Fires before the checked value is set, providing an opportunity to cancel if needed
20974          * @param {Roo.menu.CheckItem} this
20975          * @param {Boolean} checked The new checked value that will be set
20976          */
20977         "beforecheckchange" : true,
20978         /**
20979          * @event checkchange
20980          * Fires after the checked value has been set
20981          * @param {Roo.menu.CheckItem} this
20982          * @param {Boolean} checked The checked value that was set
20983          */
20984         "checkchange" : true
20985     });
20986     if(this.checkHandler){
20987         this.on('checkchange', this.checkHandler, this.scope);
20988     }
20989 };
20990 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
20991     /**
20992      * @cfg {String} group
20993      * All check items with the same group name will automatically be grouped into a single-select
20994      * radio button group (defaults to '')
20995      */
20996     /**
20997      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
20998      */
20999     itemCls : "x-menu-item x-menu-check-item",
21000     /**
21001      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
21002      */
21003     groupClass : "x-menu-group-item",
21004
21005     /**
21006      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
21007      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
21008      * initialized with checked = true will be rendered as checked.
21009      */
21010     checked: false,
21011
21012     // private
21013     ctype: "Roo.menu.CheckItem",
21014
21015     // private
21016     onRender : function(c){
21017         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
21018         if(this.group){
21019             this.el.addClass(this.groupClass);
21020         }
21021         Roo.menu.MenuMgr.registerCheckable(this);
21022         if(this.checked){
21023             this.checked = false;
21024             this.setChecked(true, true);
21025         }
21026     },
21027
21028     // private
21029     destroy : function(){
21030         if(this.rendered){
21031             Roo.menu.MenuMgr.unregisterCheckable(this);
21032         }
21033         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
21034     },
21035
21036     /**
21037      * Set the checked state of this item
21038      * @param {Boolean} checked The new checked value
21039      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
21040      */
21041     setChecked : function(state, suppressEvent){
21042         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
21043             if(this.container){
21044                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
21045             }
21046             this.checked = state;
21047             if(suppressEvent !== true){
21048                 this.fireEvent("checkchange", this, state);
21049             }
21050         }
21051     },
21052
21053     // private
21054     handleClick : function(e){
21055        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
21056            this.setChecked(!this.checked);
21057        }
21058        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
21059     }
21060 });/*
21061  * Based on:
21062  * Ext JS Library 1.1.1
21063  * Copyright(c) 2006-2007, Ext JS, LLC.
21064  *
21065  * Originally Released Under LGPL - original licence link has changed is not relivant.
21066  *
21067  * Fork - LGPL
21068  * <script type="text/javascript">
21069  */
21070  
21071 /**
21072  * @class Roo.menu.DateItem
21073  * @extends Roo.menu.Adapter
21074  * A menu item that wraps the {@link Roo.DatPicker} component.
21075  * @constructor
21076  * Creates a new DateItem
21077  * @param {Object} config Configuration options
21078  */
21079 Roo.menu.DateItem = function(config){
21080     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
21081     /** The Roo.DatePicker object @type Roo.DatePicker */
21082     this.picker = this.component;
21083     this.addEvents({select: true});
21084     
21085     this.picker.on("render", function(picker){
21086         picker.getEl().swallowEvent("click");
21087         picker.container.addClass("x-menu-date-item");
21088     });
21089
21090     this.picker.on("select", this.onSelect, this);
21091 };
21092
21093 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
21094     // private
21095     onSelect : function(picker, date){
21096         this.fireEvent("select", this, date, picker);
21097         Roo.menu.DateItem.superclass.handleClick.call(this);
21098     }
21099 });/*
21100  * Based on:
21101  * Ext JS Library 1.1.1
21102  * Copyright(c) 2006-2007, Ext JS, LLC.
21103  *
21104  * Originally Released Under LGPL - original licence link has changed is not relivant.
21105  *
21106  * Fork - LGPL
21107  * <script type="text/javascript">
21108  */
21109  
21110 /**
21111  * @class Roo.menu.ColorItem
21112  * @extends Roo.menu.Adapter
21113  * A menu item that wraps the {@link Roo.ColorPalette} component.
21114  * @constructor
21115  * Creates a new ColorItem
21116  * @param {Object} config Configuration options
21117  */
21118 Roo.menu.ColorItem = function(config){
21119     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
21120     /** The Roo.ColorPalette object @type Roo.ColorPalette */
21121     this.palette = this.component;
21122     this.relayEvents(this.palette, ["select"]);
21123     if(this.selectHandler){
21124         this.on('select', this.selectHandler, this.scope);
21125     }
21126 };
21127 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
21128  * Based on:
21129  * Ext JS Library 1.1.1
21130  * Copyright(c) 2006-2007, Ext JS, LLC.
21131  *
21132  * Originally Released Under LGPL - original licence link has changed is not relivant.
21133  *
21134  * Fork - LGPL
21135  * <script type="text/javascript">
21136  */
21137  
21138
21139 /**
21140  * @class Roo.menu.DateMenu
21141  * @extends Roo.menu.Menu
21142  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
21143  * @constructor
21144  * Creates a new DateMenu
21145  * @param {Object} config Configuration options
21146  */
21147 Roo.menu.DateMenu = function(config){
21148     Roo.menu.DateMenu.superclass.constructor.call(this, config);
21149     this.plain = true;
21150     var di = new Roo.menu.DateItem(config);
21151     this.add(di);
21152     /**
21153      * The {@link Roo.DatePicker} instance for this DateMenu
21154      * @type DatePicker
21155      */
21156     this.picker = di.picker;
21157     /**
21158      * @event select
21159      * @param {DatePicker} picker
21160      * @param {Date} date
21161      */
21162     this.relayEvents(di, ["select"]);
21163     this.on('beforeshow', function(){
21164         if(this.picker){
21165             this.picker.hideMonthPicker(false);
21166         }
21167     }, this);
21168 };
21169 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
21170     cls:'x-date-menu'
21171 });/*
21172  * Based on:
21173  * Ext JS Library 1.1.1
21174  * Copyright(c) 2006-2007, Ext JS, LLC.
21175  *
21176  * Originally Released Under LGPL - original licence link has changed is not relivant.
21177  *
21178  * Fork - LGPL
21179  * <script type="text/javascript">
21180  */
21181  
21182
21183 /**
21184  * @class Roo.menu.ColorMenu
21185  * @extends Roo.menu.Menu
21186  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
21187  * @constructor
21188  * Creates a new ColorMenu
21189  * @param {Object} config Configuration options
21190  */
21191 Roo.menu.ColorMenu = function(config){
21192     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
21193     this.plain = true;
21194     var ci = new Roo.menu.ColorItem(config);
21195     this.add(ci);
21196     /**
21197      * The {@link Roo.ColorPalette} instance for this ColorMenu
21198      * @type ColorPalette
21199      */
21200     this.palette = ci.palette;
21201     /**
21202      * @event select
21203      * @param {ColorPalette} palette
21204      * @param {String} color
21205      */
21206     this.relayEvents(ci, ["select"]);
21207 };
21208 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
21209  * Based on:
21210  * Ext JS Library 1.1.1
21211  * Copyright(c) 2006-2007, Ext JS, LLC.
21212  *
21213  * Originally Released Under LGPL - original licence link has changed is not relivant.
21214  *
21215  * Fork - LGPL
21216  * <script type="text/javascript">
21217  */
21218  
21219 /**
21220  * @class Roo.form.Field
21221  * @extends Roo.BoxComponent
21222  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
21223  * @constructor
21224  * Creates a new Field
21225  * @param {Object} config Configuration options
21226  */
21227 Roo.form.Field = function(config){
21228     Roo.form.Field.superclass.constructor.call(this, config);
21229 };
21230
21231 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
21232     /**
21233      * @cfg {String} fieldLabel Label to use when rendering a form.
21234      */
21235        /**
21236      * @cfg {String} qtip Mouse over tip
21237      */
21238      
21239     /**
21240      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
21241      */
21242     invalidClass : "x-form-invalid",
21243     /**
21244      * @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")
21245      */
21246     invalidText : "The value in this field is invalid",
21247     /**
21248      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
21249      */
21250     focusClass : "x-form-focus",
21251     /**
21252      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
21253       automatic validation (defaults to "keyup").
21254      */
21255     validationEvent : "keyup",
21256     /**
21257      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
21258      */
21259     validateOnBlur : true,
21260     /**
21261      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
21262      */
21263     validationDelay : 250,
21264     /**
21265      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21266      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
21267      */
21268     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
21269     /**
21270      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
21271      */
21272     fieldClass : "x-form-field",
21273     /**
21274      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
21275      *<pre>
21276 Value         Description
21277 -----------   ----------------------------------------------------------------------
21278 qtip          Display a quick tip when the user hovers over the field
21279 title         Display a default browser title attribute popup
21280 under         Add a block div beneath the field containing the error text
21281 side          Add an error icon to the right of the field with a popup on hover
21282 [element id]  Add the error text directly to the innerHTML of the specified element
21283 </pre>
21284      */
21285     msgTarget : 'qtip',
21286     /**
21287      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
21288      */
21289     msgFx : 'normal',
21290
21291     /**
21292      * @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.
21293      */
21294     readOnly : false,
21295
21296     /**
21297      * @cfg {Boolean} disabled True to disable the field (defaults to false).
21298      */
21299     disabled : false,
21300
21301     /**
21302      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
21303      */
21304     inputType : undefined,
21305     
21306     /**
21307      * @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).
21308          */
21309         tabIndex : undefined,
21310         
21311     // private
21312     isFormField : true,
21313
21314     // private
21315     hasFocus : false,
21316     /**
21317      * @property {Roo.Element} fieldEl
21318      * Element Containing the rendered Field (with label etc.)
21319      */
21320     /**
21321      * @cfg {Mixed} value A value to initialize this field with.
21322      */
21323     value : undefined,
21324
21325     /**
21326      * @cfg {String} name The field's HTML name attribute.
21327      */
21328     /**
21329      * @cfg {String} cls A CSS class to apply to the field's underlying element.
21330      */
21331
21332         // private ??
21333         initComponent : function(){
21334         Roo.form.Field.superclass.initComponent.call(this);
21335         this.addEvents({
21336             /**
21337              * @event focus
21338              * Fires when this field receives input focus.
21339              * @param {Roo.form.Field} this
21340              */
21341             focus : true,
21342             /**
21343              * @event blur
21344              * Fires when this field loses input focus.
21345              * @param {Roo.form.Field} this
21346              */
21347             blur : true,
21348             /**
21349              * @event specialkey
21350              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
21351              * {@link Roo.EventObject#getKey} to determine which key was pressed.
21352              * @param {Roo.form.Field} this
21353              * @param {Roo.EventObject} e The event object
21354              */
21355             specialkey : true,
21356             /**
21357              * @event change
21358              * Fires just before the field blurs if the field value has changed.
21359              * @param {Roo.form.Field} this
21360              * @param {Mixed} newValue The new value
21361              * @param {Mixed} oldValue The original value
21362              */
21363             change : true,
21364             /**
21365              * @event invalid
21366              * Fires after the field has been marked as invalid.
21367              * @param {Roo.form.Field} this
21368              * @param {String} msg The validation message
21369              */
21370             invalid : true,
21371             /**
21372              * @event valid
21373              * Fires after the field has been validated with no errors.
21374              * @param {Roo.form.Field} this
21375              */
21376             valid : true,
21377              /**
21378              * @event keyup
21379              * Fires after the key up
21380              * @param {Roo.form.Field} this
21381              * @param {Roo.EventObject}  e The event Object
21382              */
21383             keyup : true
21384         });
21385     },
21386
21387     /**
21388      * Returns the name attribute of the field if available
21389      * @return {String} name The field name
21390      */
21391     getName: function(){
21392          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
21393     },
21394
21395     // private
21396     onRender : function(ct, position){
21397         Roo.form.Field.superclass.onRender.call(this, ct, position);
21398         if(!this.el){
21399             var cfg = this.getAutoCreate();
21400             if(!cfg.name){
21401                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
21402             }
21403             if (!cfg.name.length) {
21404                 delete cfg.name;
21405             }
21406             if(this.inputType){
21407                 cfg.type = this.inputType;
21408             }
21409             this.el = ct.createChild(cfg, position);
21410         }
21411         var type = this.el.dom.type;
21412         if(type){
21413             if(type == 'password'){
21414                 type = 'text';
21415             }
21416             this.el.addClass('x-form-'+type);
21417         }
21418         if(this.readOnly){
21419             this.el.dom.readOnly = true;
21420         }
21421         if(this.tabIndex !== undefined){
21422             this.el.dom.setAttribute('tabIndex', this.tabIndex);
21423         }
21424
21425         this.el.addClass([this.fieldClass, this.cls]);
21426         this.initValue();
21427     },
21428
21429     /**
21430      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
21431      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
21432      * @return {Roo.form.Field} this
21433      */
21434     applyTo : function(target){
21435         this.allowDomMove = false;
21436         this.el = Roo.get(target);
21437         this.render(this.el.dom.parentNode);
21438         return this;
21439     },
21440
21441     // private
21442     initValue : function(){
21443         if(this.value !== undefined){
21444             this.setValue(this.value);
21445         }else if(this.el.dom.value.length > 0){
21446             this.setValue(this.el.dom.value);
21447         }
21448     },
21449
21450     /**
21451      * Returns true if this field has been changed since it was originally loaded and is not disabled.
21452      */
21453     isDirty : function() {
21454         if(this.disabled) {
21455             return false;
21456         }
21457         return String(this.getValue()) !== String(this.originalValue);
21458     },
21459
21460     // private
21461     afterRender : function(){
21462         Roo.form.Field.superclass.afterRender.call(this);
21463         this.initEvents();
21464     },
21465
21466     // private
21467     fireKey : function(e){
21468         //Roo.log('field ' + e.getKey());
21469         if(e.isNavKeyPress()){
21470             this.fireEvent("specialkey", this, e);
21471         }
21472     },
21473
21474     /**
21475      * Resets the current field value to the originally loaded value and clears any validation messages
21476      */
21477     reset : function(){
21478         this.setValue(this.originalValue);
21479         this.clearInvalid();
21480     },
21481
21482     // private
21483     initEvents : function(){
21484         // safari killled keypress - so keydown is now used..
21485         this.el.on("keydown" , this.fireKey,  this);
21486         this.el.on("focus", this.onFocus,  this);
21487         this.el.on("blur", this.onBlur,  this);
21488         this.el.relayEvent('keyup', this);
21489
21490         // reference to original value for reset
21491         this.originalValue = this.getValue();
21492     },
21493
21494     // private
21495     onFocus : function(){
21496         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21497             this.el.addClass(this.focusClass);
21498         }
21499         if(!this.hasFocus){
21500             this.hasFocus = true;
21501             this.startValue = this.getValue();
21502             this.fireEvent("focus", this);
21503         }
21504     },
21505
21506     beforeBlur : Roo.emptyFn,
21507
21508     // private
21509     onBlur : function(){
21510         this.beforeBlur();
21511         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21512             this.el.removeClass(this.focusClass);
21513         }
21514         this.hasFocus = false;
21515         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
21516             this.validate();
21517         }
21518         var v = this.getValue();
21519         if(String(v) !== String(this.startValue)){
21520             this.fireEvent('change', this, v, this.startValue);
21521         }
21522         this.fireEvent("blur", this);
21523     },
21524
21525     /**
21526      * Returns whether or not the field value is currently valid
21527      * @param {Boolean} preventMark True to disable marking the field invalid
21528      * @return {Boolean} True if the value is valid, else false
21529      */
21530     isValid : function(preventMark){
21531         if(this.disabled){
21532             return true;
21533         }
21534         var restore = this.preventMark;
21535         this.preventMark = preventMark === true;
21536         var v = this.validateValue(this.processValue(this.getRawValue()));
21537         this.preventMark = restore;
21538         return v;
21539     },
21540
21541     /**
21542      * Validates the field value
21543      * @return {Boolean} True if the value is valid, else false
21544      */
21545     validate : function(){
21546         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
21547             this.clearInvalid();
21548             return true;
21549         }
21550         return false;
21551     },
21552
21553     processValue : function(value){
21554         return value;
21555     },
21556
21557     // private
21558     // Subclasses should provide the validation implementation by overriding this
21559     validateValue : function(value){
21560         return true;
21561     },
21562
21563     /**
21564      * Mark this field as invalid
21565      * @param {String} msg The validation message
21566      */
21567     markInvalid : function(msg){
21568         if(!this.rendered || this.preventMark){ // not rendered
21569             return;
21570         }
21571         this.el.addClass(this.invalidClass);
21572         msg = msg || this.invalidText;
21573         switch(this.msgTarget){
21574             case 'qtip':
21575                 this.el.dom.qtip = msg;
21576                 this.el.dom.qclass = 'x-form-invalid-tip';
21577                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
21578                     Roo.QuickTips.enable();
21579                 }
21580                 break;
21581             case 'title':
21582                 this.el.dom.title = msg;
21583                 break;
21584             case 'under':
21585                 if(!this.errorEl){
21586                     var elp = this.el.findParent('.x-form-element', 5, true);
21587                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
21588                     this.errorEl.setWidth(elp.getWidth(true)-20);
21589                 }
21590                 this.errorEl.update(msg);
21591                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
21592                 break;
21593             case 'side':
21594                 if(!this.errorIcon){
21595                     var elp = this.el.findParent('.x-form-element', 5, true);
21596                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
21597                 }
21598                 this.alignErrorIcon();
21599                 this.errorIcon.dom.qtip = msg;
21600                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
21601                 this.errorIcon.show();
21602                 this.on('resize', this.alignErrorIcon, this);
21603                 break;
21604             default:
21605                 var t = Roo.getDom(this.msgTarget);
21606                 t.innerHTML = msg;
21607                 t.style.display = this.msgDisplay;
21608                 break;
21609         }
21610         this.fireEvent('invalid', this, msg);
21611     },
21612
21613     // private
21614     alignErrorIcon : function(){
21615         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
21616     },
21617
21618     /**
21619      * Clear any invalid styles/messages for this field
21620      */
21621     clearInvalid : function(){
21622         if(!this.rendered || this.preventMark){ // not rendered
21623             return;
21624         }
21625         this.el.removeClass(this.invalidClass);
21626         switch(this.msgTarget){
21627             case 'qtip':
21628                 this.el.dom.qtip = '';
21629                 break;
21630             case 'title':
21631                 this.el.dom.title = '';
21632                 break;
21633             case 'under':
21634                 if(this.errorEl){
21635                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
21636                 }
21637                 break;
21638             case 'side':
21639                 if(this.errorIcon){
21640                     this.errorIcon.dom.qtip = '';
21641                     this.errorIcon.hide();
21642                     this.un('resize', this.alignErrorIcon, this);
21643                 }
21644                 break;
21645             default:
21646                 var t = Roo.getDom(this.msgTarget);
21647                 t.innerHTML = '';
21648                 t.style.display = 'none';
21649                 break;
21650         }
21651         this.fireEvent('valid', this);
21652     },
21653
21654     /**
21655      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
21656      * @return {Mixed} value The field value
21657      */
21658     getRawValue : function(){
21659         var v = this.el.getValue();
21660         
21661         return v;
21662     },
21663
21664     /**
21665      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
21666      * @return {Mixed} value The field value
21667      */
21668     getValue : function(){
21669         var v = this.el.getValue();
21670          
21671         return v;
21672     },
21673
21674     /**
21675      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
21676      * @param {Mixed} value The value to set
21677      */
21678     setRawValue : function(v){
21679         return this.el.dom.value = (v === null || v === undefined ? '' : v);
21680     },
21681
21682     /**
21683      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
21684      * @param {Mixed} value The value to set
21685      */
21686     setValue : function(v){
21687         this.value = v;
21688         if(this.rendered){
21689             this.el.dom.value = (v === null || v === undefined ? '' : v);
21690              this.validate();
21691         }
21692     },
21693
21694     adjustSize : function(w, h){
21695         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21696         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21697         return s;
21698     },
21699
21700     adjustWidth : function(tag, w){
21701         tag = tag.toLowerCase();
21702         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21703             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21704                 if(tag == 'input'){
21705                     return w + 2;
21706                 }
21707                 if(tag == 'textarea'){
21708                     return w-2;
21709                 }
21710             }else if(Roo.isOpera){
21711                 if(tag == 'input'){
21712                     return w + 2;
21713                 }
21714                 if(tag == 'textarea'){
21715                     return w-2;
21716                 }
21717             }
21718         }
21719         return w;
21720     }
21721 });
21722
21723
21724 // anything other than normal should be considered experimental
21725 Roo.form.Field.msgFx = {
21726     normal : {
21727         show: function(msgEl, f){
21728             msgEl.setDisplayed('block');
21729         },
21730
21731         hide : function(msgEl, f){
21732             msgEl.setDisplayed(false).update('');
21733         }
21734     },
21735
21736     slide : {
21737         show: function(msgEl, f){
21738             msgEl.slideIn('t', {stopFx:true});
21739         },
21740
21741         hide : function(msgEl, f){
21742             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21743         }
21744     },
21745
21746     slideRight : {
21747         show: function(msgEl, f){
21748             msgEl.fixDisplay();
21749             msgEl.alignTo(f.el, 'tl-tr');
21750             msgEl.slideIn('l', {stopFx:true});
21751         },
21752
21753         hide : function(msgEl, f){
21754             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21755         }
21756     }
21757 };/*
21758  * Based on:
21759  * Ext JS Library 1.1.1
21760  * Copyright(c) 2006-2007, Ext JS, LLC.
21761  *
21762  * Originally Released Under LGPL - original licence link has changed is not relivant.
21763  *
21764  * Fork - LGPL
21765  * <script type="text/javascript">
21766  */
21767  
21768
21769 /**
21770  * @class Roo.form.TextField
21771  * @extends Roo.form.Field
21772  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
21773  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21774  * @constructor
21775  * Creates a new TextField
21776  * @param {Object} config Configuration options
21777  */
21778 Roo.form.TextField = function(config){
21779     Roo.form.TextField.superclass.constructor.call(this, config);
21780     this.addEvents({
21781         /**
21782          * @event autosize
21783          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
21784          * according to the default logic, but this event provides a hook for the developer to apply additional
21785          * logic at runtime to resize the field if needed.
21786              * @param {Roo.form.Field} this This text field
21787              * @param {Number} width The new field width
21788              */
21789         autosize : true
21790     });
21791 };
21792
21793 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
21794     /**
21795      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21796      */
21797     grow : false,
21798     /**
21799      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21800      */
21801     growMin : 30,
21802     /**
21803      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21804      */
21805     growMax : 800,
21806     /**
21807      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21808      */
21809     vtype : null,
21810     /**
21811      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21812      */
21813     maskRe : null,
21814     /**
21815      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21816      */
21817     disableKeyFilter : false,
21818     /**
21819      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21820      */
21821     allowBlank : true,
21822     /**
21823      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21824      */
21825     minLength : 0,
21826     /**
21827      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21828      */
21829     maxLength : Number.MAX_VALUE,
21830     /**
21831      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21832      */
21833     minLengthText : "The minimum length for this field is {0}",
21834     /**
21835      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21836      */
21837     maxLengthText : "The maximum length for this field is {0}",
21838     /**
21839      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21840      */
21841     selectOnFocus : false,
21842     /**
21843      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21844      */
21845     blankText : "This field is required",
21846     /**
21847      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21848      * If available, this function will be called only after the basic validators all return true, and will be passed the
21849      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21850      */
21851     validator : null,
21852     /**
21853      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21854      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21855      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
21856      */
21857     regex : null,
21858     /**
21859      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21860      */
21861     regexText : "",
21862     /**
21863      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
21864      */
21865     emptyText : null,
21866    
21867
21868     // private
21869     initEvents : function()
21870     {
21871         if (this.emptyText) {
21872             this.el.attr('placeholder', this.emptyText);
21873         }
21874         
21875         Roo.form.TextField.superclass.initEvents.call(this);
21876         if(this.validationEvent == 'keyup'){
21877             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21878             this.el.on('keyup', this.filterValidation, this);
21879         }
21880         else if(this.validationEvent !== false){
21881             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21882         }
21883         
21884         if(this.selectOnFocus){
21885             this.on("focus", this.preFocus, this);
21886             
21887         }
21888         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21889             this.el.on("keypress", this.filterKeys, this);
21890         }
21891         if(this.grow){
21892             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
21893             this.el.on("click", this.autoSize,  this);
21894         }
21895         if(this.el.is('input[type=password]') && Roo.isSafari){
21896             this.el.on('keydown', this.SafariOnKeyDown, this);
21897         }
21898     },
21899
21900     processValue : function(value){
21901         if(this.stripCharsRe){
21902             var newValue = value.replace(this.stripCharsRe, '');
21903             if(newValue !== value){
21904                 this.setRawValue(newValue);
21905                 return newValue;
21906             }
21907         }
21908         return value;
21909     },
21910
21911     filterValidation : function(e){
21912         if(!e.isNavKeyPress()){
21913             this.validationTask.delay(this.validationDelay);
21914         }
21915     },
21916
21917     // private
21918     onKeyUp : function(e){
21919         if(!e.isNavKeyPress()){
21920             this.autoSize();
21921         }
21922     },
21923
21924     /**
21925      * Resets the current field value to the originally-loaded value and clears any validation messages.
21926      *  
21927      */
21928     reset : function(){
21929         Roo.form.TextField.superclass.reset.call(this);
21930        
21931     },
21932
21933     
21934     // private
21935     preFocus : function(){
21936         
21937         if(this.selectOnFocus){
21938             this.el.dom.select();
21939         }
21940     },
21941
21942     
21943     // private
21944     filterKeys : function(e){
21945         var k = e.getKey();
21946         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21947             return;
21948         }
21949         var c = e.getCharCode(), cc = String.fromCharCode(c);
21950         if(Roo.isIE && (e.isSpecialKey() || !cc)){
21951             return;
21952         }
21953         if(!this.maskRe.test(cc)){
21954             e.stopEvent();
21955         }
21956     },
21957
21958     setValue : function(v){
21959         
21960         Roo.form.TextField.superclass.setValue.apply(this, arguments);
21961         
21962         this.autoSize();
21963     },
21964
21965     /**
21966      * Validates a value according to the field's validation rules and marks the field as invalid
21967      * if the validation fails
21968      * @param {Mixed} value The value to validate
21969      * @return {Boolean} True if the value is valid, else false
21970      */
21971     validateValue : function(value){
21972         if(value.length < 1)  { // if it's blank
21973              if(this.allowBlank){
21974                 this.clearInvalid();
21975                 return true;
21976              }else{
21977                 this.markInvalid(this.blankText);
21978                 return false;
21979              }
21980         }
21981         if(value.length < this.minLength){
21982             this.markInvalid(String.format(this.minLengthText, this.minLength));
21983             return false;
21984         }
21985         if(value.length > this.maxLength){
21986             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
21987             return false;
21988         }
21989         if(this.vtype){
21990             var vt = Roo.form.VTypes;
21991             if(!vt[this.vtype](value, this)){
21992                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
21993                 return false;
21994             }
21995         }
21996         if(typeof this.validator == "function"){
21997             var msg = this.validator(value);
21998             if(msg !== true){
21999                 this.markInvalid(msg);
22000                 return false;
22001             }
22002         }
22003         if(this.regex && !this.regex.test(value)){
22004             this.markInvalid(this.regexText);
22005             return false;
22006         }
22007         return true;
22008     },
22009
22010     /**
22011      * Selects text in this field
22012      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
22013      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
22014      */
22015     selectText : function(start, end){
22016         var v = this.getRawValue();
22017         if(v.length > 0){
22018             start = start === undefined ? 0 : start;
22019             end = end === undefined ? v.length : end;
22020             var d = this.el.dom;
22021             if(d.setSelectionRange){
22022                 d.setSelectionRange(start, end);
22023             }else if(d.createTextRange){
22024                 var range = d.createTextRange();
22025                 range.moveStart("character", start);
22026                 range.moveEnd("character", v.length-end);
22027                 range.select();
22028             }
22029         }
22030     },
22031
22032     /**
22033      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
22034      * This only takes effect if grow = true, and fires the autosize event.
22035      */
22036     autoSize : function(){
22037         if(!this.grow || !this.rendered){
22038             return;
22039         }
22040         if(!this.metrics){
22041             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
22042         }
22043         var el = this.el;
22044         var v = el.dom.value;
22045         var d = document.createElement('div');
22046         d.appendChild(document.createTextNode(v));
22047         v = d.innerHTML;
22048         d = null;
22049         v += "&#160;";
22050         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
22051         this.el.setWidth(w);
22052         this.fireEvent("autosize", this, w);
22053     },
22054     
22055     // private
22056     SafariOnKeyDown : function(event)
22057     {
22058         // this is a workaround for a password hang bug on chrome/ webkit.
22059         
22060         var isSelectAll = false;
22061         
22062         if(this.el.dom.selectionEnd > 0){
22063             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
22064         }
22065         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
22066             event.preventDefault();
22067             this.setValue('');
22068             return;
22069         }
22070         
22071         if(isSelectAll){ // backspace and delete key
22072             
22073             event.preventDefault();
22074             // this is very hacky as keydown always get's upper case.
22075             //
22076             var cc = String.fromCharCode(event.getCharCode());
22077             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
22078             
22079         }
22080         
22081         
22082     }
22083 });/*
22084  * Based on:
22085  * Ext JS Library 1.1.1
22086  * Copyright(c) 2006-2007, Ext JS, LLC.
22087  *
22088  * Originally Released Under LGPL - original licence link has changed is not relivant.
22089  *
22090  * Fork - LGPL
22091  * <script type="text/javascript">
22092  */
22093  
22094 /**
22095  * @class Roo.form.Hidden
22096  * @extends Roo.form.TextField
22097  * Simple Hidden element used on forms 
22098  * 
22099  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
22100  * 
22101  * @constructor
22102  * Creates a new Hidden form element.
22103  * @param {Object} config Configuration options
22104  */
22105
22106
22107
22108 // easy hidden field...
22109 Roo.form.Hidden = function(config){
22110     Roo.form.Hidden.superclass.constructor.call(this, config);
22111 };
22112   
22113 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
22114     fieldLabel:      '',
22115     inputType:      'hidden',
22116     width:          50,
22117     allowBlank:     true,
22118     labelSeparator: '',
22119     hidden:         true,
22120     itemCls :       'x-form-item-display-none'
22121
22122
22123 });
22124
22125
22126 /*
22127  * Based on:
22128  * Ext JS Library 1.1.1
22129  * Copyright(c) 2006-2007, Ext JS, LLC.
22130  *
22131  * Originally Released Under LGPL - original licence link has changed is not relivant.
22132  *
22133  * Fork - LGPL
22134  * <script type="text/javascript">
22135  */
22136  
22137 /**
22138  * @class Roo.form.TriggerField
22139  * @extends Roo.form.TextField
22140  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
22141  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
22142  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
22143  * for which you can provide a custom implementation.  For example:
22144  * <pre><code>
22145 var trigger = new Roo.form.TriggerField();
22146 trigger.onTriggerClick = myTriggerFn;
22147 trigger.applyTo('my-field');
22148 </code></pre>
22149  *
22150  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
22151  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
22152  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
22153  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
22154  * @constructor
22155  * Create a new TriggerField.
22156  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
22157  * to the base TextField)
22158  */
22159 Roo.form.TriggerField = function(config){
22160     this.mimicing = false;
22161     Roo.form.TriggerField.superclass.constructor.call(this, config);
22162 };
22163
22164 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
22165     /**
22166      * @cfg {String} triggerClass A CSS class to apply to the trigger
22167      */
22168     /**
22169      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
22170      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
22171      */
22172     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
22173     /**
22174      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
22175      */
22176     hideTrigger:false,
22177
22178     /** @cfg {Boolean} grow @hide */
22179     /** @cfg {Number} growMin @hide */
22180     /** @cfg {Number} growMax @hide */
22181
22182     /**
22183      * @hide 
22184      * @method
22185      */
22186     autoSize: Roo.emptyFn,
22187     // private
22188     monitorTab : true,
22189     // private
22190     deferHeight : true,
22191
22192     
22193     actionMode : 'wrap',
22194     // private
22195     onResize : function(w, h){
22196         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
22197         if(typeof w == 'number'){
22198             var x = w - this.trigger.getWidth();
22199             this.el.setWidth(this.adjustWidth('input', x));
22200             this.trigger.setStyle('left', x+'px');
22201         }
22202     },
22203
22204     // private
22205     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22206
22207     // private
22208     getResizeEl : function(){
22209         return this.wrap;
22210     },
22211
22212     // private
22213     getPositionEl : function(){
22214         return this.wrap;
22215     },
22216
22217     // private
22218     alignErrorIcon : function(){
22219         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
22220     },
22221
22222     // private
22223     onRender : function(ct, position){
22224         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
22225         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
22226         this.trigger = this.wrap.createChild(this.triggerConfig ||
22227                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
22228         if(this.hideTrigger){
22229             this.trigger.setDisplayed(false);
22230         }
22231         this.initTrigger();
22232         if(!this.width){
22233             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
22234         }
22235     },
22236
22237     // private
22238     initTrigger : function(){
22239         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
22240         this.trigger.addClassOnOver('x-form-trigger-over');
22241         this.trigger.addClassOnClick('x-form-trigger-click');
22242     },
22243
22244     // private
22245     onDestroy : function(){
22246         if(this.trigger){
22247             this.trigger.removeAllListeners();
22248             this.trigger.remove();
22249         }
22250         if(this.wrap){
22251             this.wrap.remove();
22252         }
22253         Roo.form.TriggerField.superclass.onDestroy.call(this);
22254     },
22255
22256     // private
22257     onFocus : function(){
22258         Roo.form.TriggerField.superclass.onFocus.call(this);
22259         if(!this.mimicing){
22260             this.wrap.addClass('x-trigger-wrap-focus');
22261             this.mimicing = true;
22262             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
22263             if(this.monitorTab){
22264                 this.el.on("keydown", this.checkTab, this);
22265             }
22266         }
22267     },
22268
22269     // private
22270     checkTab : function(e){
22271         if(e.getKey() == e.TAB){
22272             this.triggerBlur();
22273         }
22274     },
22275
22276     // private
22277     onBlur : function(){
22278         // do nothing
22279     },
22280
22281     // private
22282     mimicBlur : function(e, t){
22283         if(!this.wrap.contains(t) && this.validateBlur()){
22284             this.triggerBlur();
22285         }
22286     },
22287
22288     // private
22289     triggerBlur : function(){
22290         this.mimicing = false;
22291         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
22292         if(this.monitorTab){
22293             this.el.un("keydown", this.checkTab, this);
22294         }
22295         this.wrap.removeClass('x-trigger-wrap-focus');
22296         Roo.form.TriggerField.superclass.onBlur.call(this);
22297     },
22298
22299     // private
22300     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
22301     validateBlur : function(e, t){
22302         return true;
22303     },
22304
22305     // private
22306     onDisable : function(){
22307         Roo.form.TriggerField.superclass.onDisable.call(this);
22308         if(this.wrap){
22309             this.wrap.addClass('x-item-disabled');
22310         }
22311     },
22312
22313     // private
22314     onEnable : function(){
22315         Roo.form.TriggerField.superclass.onEnable.call(this);
22316         if(this.wrap){
22317             this.wrap.removeClass('x-item-disabled');
22318         }
22319     },
22320
22321     // private
22322     onShow : function(){
22323         var ae = this.getActionEl();
22324         
22325         if(ae){
22326             ae.dom.style.display = '';
22327             ae.dom.style.visibility = 'visible';
22328         }
22329     },
22330
22331     // private
22332     
22333     onHide : function(){
22334         var ae = this.getActionEl();
22335         ae.dom.style.display = 'none';
22336     },
22337
22338     /**
22339      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
22340      * by an implementing function.
22341      * @method
22342      * @param {EventObject} e
22343      */
22344     onTriggerClick : Roo.emptyFn
22345 });
22346
22347 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
22348 // to be extended by an implementing class.  For an example of implementing this class, see the custom
22349 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
22350 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
22351     initComponent : function(){
22352         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
22353
22354         this.triggerConfig = {
22355             tag:'span', cls:'x-form-twin-triggers', cn:[
22356             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
22357             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
22358         ]};
22359     },
22360
22361     getTrigger : function(index){
22362         return this.triggers[index];
22363     },
22364
22365     initTrigger : function(){
22366         var ts = this.trigger.select('.x-form-trigger', true);
22367         this.wrap.setStyle('overflow', 'hidden');
22368         var triggerField = this;
22369         ts.each(function(t, all, index){
22370             t.hide = function(){
22371                 var w = triggerField.wrap.getWidth();
22372                 this.dom.style.display = 'none';
22373                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
22374             };
22375             t.show = function(){
22376                 var w = triggerField.wrap.getWidth();
22377                 this.dom.style.display = '';
22378                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
22379             };
22380             var triggerIndex = 'Trigger'+(index+1);
22381
22382             if(this['hide'+triggerIndex]){
22383                 t.dom.style.display = 'none';
22384             }
22385             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
22386             t.addClassOnOver('x-form-trigger-over');
22387             t.addClassOnClick('x-form-trigger-click');
22388         }, this);
22389         this.triggers = ts.elements;
22390     },
22391
22392     onTrigger1Click : Roo.emptyFn,
22393     onTrigger2Click : Roo.emptyFn
22394 });/*
22395  * Based on:
22396  * Ext JS Library 1.1.1
22397  * Copyright(c) 2006-2007, Ext JS, LLC.
22398  *
22399  * Originally Released Under LGPL - original licence link has changed is not relivant.
22400  *
22401  * Fork - LGPL
22402  * <script type="text/javascript">
22403  */
22404  
22405 /**
22406  * @class Roo.form.TextArea
22407  * @extends Roo.form.TextField
22408  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
22409  * support for auto-sizing.
22410  * @constructor
22411  * Creates a new TextArea
22412  * @param {Object} config Configuration options
22413  */
22414 Roo.form.TextArea = function(config){
22415     Roo.form.TextArea.superclass.constructor.call(this, config);
22416     // these are provided exchanges for backwards compat
22417     // minHeight/maxHeight were replaced by growMin/growMax to be
22418     // compatible with TextField growing config values
22419     if(this.minHeight !== undefined){
22420         this.growMin = this.minHeight;
22421     }
22422     if(this.maxHeight !== undefined){
22423         this.growMax = this.maxHeight;
22424     }
22425 };
22426
22427 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
22428     /**
22429      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
22430      */
22431     growMin : 60,
22432     /**
22433      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
22434      */
22435     growMax: 1000,
22436     /**
22437      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
22438      * in the field (equivalent to setting overflow: hidden, defaults to false)
22439      */
22440     preventScrollbars: false,
22441     /**
22442      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
22443      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
22444      */
22445
22446     // private
22447     onRender : function(ct, position){
22448         if(!this.el){
22449             this.defaultAutoCreate = {
22450                 tag: "textarea",
22451                 style:"width:300px;height:60px;",
22452                 autocomplete: "off"
22453             };
22454         }
22455         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
22456         if(this.grow){
22457             this.textSizeEl = Roo.DomHelper.append(document.body, {
22458                 tag: "pre", cls: "x-form-grow-sizer"
22459             });
22460             if(this.preventScrollbars){
22461                 this.el.setStyle("overflow", "hidden");
22462             }
22463             this.el.setHeight(this.growMin);
22464         }
22465     },
22466
22467     onDestroy : function(){
22468         if(this.textSizeEl){
22469             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
22470         }
22471         Roo.form.TextArea.superclass.onDestroy.call(this);
22472     },
22473
22474     // private
22475     onKeyUp : function(e){
22476         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
22477             this.autoSize();
22478         }
22479     },
22480
22481     /**
22482      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
22483      * This only takes effect if grow = true, and fires the autosize event if the height changes.
22484      */
22485     autoSize : function(){
22486         if(!this.grow || !this.textSizeEl){
22487             return;
22488         }
22489         var el = this.el;
22490         var v = el.dom.value;
22491         var ts = this.textSizeEl;
22492
22493         ts.innerHTML = '';
22494         ts.appendChild(document.createTextNode(v));
22495         v = ts.innerHTML;
22496
22497         Roo.fly(ts).setWidth(this.el.getWidth());
22498         if(v.length < 1){
22499             v = "&#160;&#160;";
22500         }else{
22501             if(Roo.isIE){
22502                 v = v.replace(/\n/g, '<p>&#160;</p>');
22503             }
22504             v += "&#160;\n&#160;";
22505         }
22506         ts.innerHTML = v;
22507         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
22508         if(h != this.lastHeight){
22509             this.lastHeight = h;
22510             this.el.setHeight(h);
22511             this.fireEvent("autosize", this, h);
22512         }
22513     }
22514 });/*
22515  * Based on:
22516  * Ext JS Library 1.1.1
22517  * Copyright(c) 2006-2007, Ext JS, LLC.
22518  *
22519  * Originally Released Under LGPL - original licence link has changed is not relivant.
22520  *
22521  * Fork - LGPL
22522  * <script type="text/javascript">
22523  */
22524  
22525
22526 /**
22527  * @class Roo.form.NumberField
22528  * @extends Roo.form.TextField
22529  * Numeric text field that provides automatic keystroke filtering and numeric validation.
22530  * @constructor
22531  * Creates a new NumberField
22532  * @param {Object} config Configuration options
22533  */
22534 Roo.form.NumberField = function(config){
22535     Roo.form.NumberField.superclass.constructor.call(this, config);
22536 };
22537
22538 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
22539     /**
22540      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
22541      */
22542     fieldClass: "x-form-field x-form-num-field",
22543     /**
22544      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
22545      */
22546     allowDecimals : true,
22547     /**
22548      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
22549      */
22550     decimalSeparator : ".",
22551     /**
22552      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
22553      */
22554     decimalPrecision : 2,
22555     /**
22556      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
22557      */
22558     allowNegative : true,
22559     /**
22560      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
22561      */
22562     minValue : Number.NEGATIVE_INFINITY,
22563     /**
22564      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
22565      */
22566     maxValue : Number.MAX_VALUE,
22567     /**
22568      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
22569      */
22570     minText : "The minimum value for this field is {0}",
22571     /**
22572      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
22573      */
22574     maxText : "The maximum value for this field is {0}",
22575     /**
22576      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
22577      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
22578      */
22579     nanText : "{0} is not a valid number",
22580
22581     // private
22582     initEvents : function(){
22583         Roo.form.NumberField.superclass.initEvents.call(this);
22584         var allowed = "0123456789";
22585         if(this.allowDecimals){
22586             allowed += this.decimalSeparator;
22587         }
22588         if(this.allowNegative){
22589             allowed += "-";
22590         }
22591         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
22592         var keyPress = function(e){
22593             var k = e.getKey();
22594             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
22595                 return;
22596             }
22597             var c = e.getCharCode();
22598             if(allowed.indexOf(String.fromCharCode(c)) === -1){
22599                 e.stopEvent();
22600             }
22601         };
22602         this.el.on("keypress", keyPress, this);
22603     },
22604
22605     // private
22606     validateValue : function(value){
22607         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
22608             return false;
22609         }
22610         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22611              return true;
22612         }
22613         var num = this.parseValue(value);
22614         if(isNaN(num)){
22615             this.markInvalid(String.format(this.nanText, value));
22616             return false;
22617         }
22618         if(num < this.minValue){
22619             this.markInvalid(String.format(this.minText, this.minValue));
22620             return false;
22621         }
22622         if(num > this.maxValue){
22623             this.markInvalid(String.format(this.maxText, this.maxValue));
22624             return false;
22625         }
22626         return true;
22627     },
22628
22629     getValue : function(){
22630         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
22631     },
22632
22633     // private
22634     parseValue : function(value){
22635         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
22636         return isNaN(value) ? '' : value;
22637     },
22638
22639     // private
22640     fixPrecision : function(value){
22641         var nan = isNaN(value);
22642         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
22643             return nan ? '' : value;
22644         }
22645         return parseFloat(value).toFixed(this.decimalPrecision);
22646     },
22647
22648     setValue : function(v){
22649         v = this.fixPrecision(v);
22650         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
22651     },
22652
22653     // private
22654     decimalPrecisionFcn : function(v){
22655         return Math.floor(v);
22656     },
22657
22658     beforeBlur : function(){
22659         var v = this.parseValue(this.getRawValue());
22660         if(v){
22661             this.setValue(v);
22662         }
22663     }
22664 });/*
22665  * Based on:
22666  * Ext JS Library 1.1.1
22667  * Copyright(c) 2006-2007, Ext JS, LLC.
22668  *
22669  * Originally Released Under LGPL - original licence link has changed is not relivant.
22670  *
22671  * Fork - LGPL
22672  * <script type="text/javascript">
22673  */
22674  
22675 /**
22676  * @class Roo.form.DateField
22677  * @extends Roo.form.TriggerField
22678  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22679 * @constructor
22680 * Create a new DateField
22681 * @param {Object} config
22682  */
22683 Roo.form.DateField = function(config){
22684     Roo.form.DateField.superclass.constructor.call(this, config);
22685     
22686       this.addEvents({
22687          
22688         /**
22689          * @event select
22690          * Fires when a date is selected
22691              * @param {Roo.form.DateField} combo This combo box
22692              * @param {Date} date The date selected
22693              */
22694         'select' : true
22695          
22696     });
22697     
22698     
22699     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22700     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22701     this.ddMatch = null;
22702     if(this.disabledDates){
22703         var dd = this.disabledDates;
22704         var re = "(?:";
22705         for(var i = 0; i < dd.length; i++){
22706             re += dd[i];
22707             if(i != dd.length-1) re += "|";
22708         }
22709         this.ddMatch = new RegExp(re + ")");
22710     }
22711 };
22712
22713 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
22714     /**
22715      * @cfg {String} format
22716      * The default date format string which can be overriden for localization support.  The format must be
22717      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22718      */
22719     format : "m/d/y",
22720     /**
22721      * @cfg {String} altFormats
22722      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22723      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22724      */
22725     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22726     /**
22727      * @cfg {Array} disabledDays
22728      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22729      */
22730     disabledDays : null,
22731     /**
22732      * @cfg {String} disabledDaysText
22733      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22734      */
22735     disabledDaysText : "Disabled",
22736     /**
22737      * @cfg {Array} disabledDates
22738      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22739      * expression so they are very powerful. Some examples:
22740      * <ul>
22741      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22742      * <li>["03/08", "09/16"] would disable those days for every year</li>
22743      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22744      * <li>["03/../2006"] would disable every day in March 2006</li>
22745      * <li>["^03"] would disable every day in every March</li>
22746      * </ul>
22747      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22748      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22749      */
22750     disabledDates : null,
22751     /**
22752      * @cfg {String} disabledDatesText
22753      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22754      */
22755     disabledDatesText : "Disabled",
22756     /**
22757      * @cfg {Date/String} minValue
22758      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22759      * valid format (defaults to null).
22760      */
22761     minValue : null,
22762     /**
22763      * @cfg {Date/String} maxValue
22764      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22765      * valid format (defaults to null).
22766      */
22767     maxValue : null,
22768     /**
22769      * @cfg {String} minText
22770      * The error text to display when the date in the cell is before minValue (defaults to
22771      * 'The date in this field must be after {minValue}').
22772      */
22773     minText : "The date in this field must be equal to or after {0}",
22774     /**
22775      * @cfg {String} maxText
22776      * The error text to display when the date in the cell is after maxValue (defaults to
22777      * 'The date in this field must be before {maxValue}').
22778      */
22779     maxText : "The date in this field must be equal to or before {0}",
22780     /**
22781      * @cfg {String} invalidText
22782      * The error text to display when the date in the field is invalid (defaults to
22783      * '{value} is not a valid date - it must be in the format {format}').
22784      */
22785     invalidText : "{0} is not a valid date - it must be in the format {1}",
22786     /**
22787      * @cfg {String} triggerClass
22788      * An additional CSS class used to style the trigger button.  The trigger will always get the
22789      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22790      * which displays a calendar icon).
22791      */
22792     triggerClass : 'x-form-date-trigger',
22793     
22794
22795     /**
22796      * @cfg {Boolean} useIso
22797      * if enabled, then the date field will use a hidden field to store the 
22798      * real value as iso formated date. default (false)
22799      */ 
22800     useIso : false,
22801     /**
22802      * @cfg {String/Object} autoCreate
22803      * A DomHelper element spec, or true for a default element spec (defaults to
22804      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22805      */ 
22806     // private
22807     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22808     
22809     // private
22810     hiddenField: false,
22811     
22812     onRender : function(ct, position)
22813     {
22814         Roo.form.DateField.superclass.onRender.call(this, ct, position);
22815         if (this.useIso) {
22816             //this.el.dom.removeAttribute('name'); 
22817             Roo.log("Changing name?");
22818             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
22819             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22820                     'before', true);
22821             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
22822             // prevent input submission
22823             this.hiddenName = this.name;
22824         }
22825             
22826             
22827     },
22828     
22829     // private
22830     validateValue : function(value)
22831     {
22832         value = this.formatDate(value);
22833         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22834             Roo.log('super failed');
22835             return false;
22836         }
22837         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22838              return true;
22839         }
22840         var svalue = value;
22841         value = this.parseDate(value);
22842         if(!value){
22843             Roo.log('parse date failed' + svalue);
22844             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22845             return false;
22846         }
22847         var time = value.getTime();
22848         if(this.minValue && time < this.minValue.getTime()){
22849             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22850             return false;
22851         }
22852         if(this.maxValue && time > this.maxValue.getTime()){
22853             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22854             return false;
22855         }
22856         if(this.disabledDays){
22857             var day = value.getDay();
22858             for(var i = 0; i < this.disabledDays.length; i++) {
22859                 if(day === this.disabledDays[i]){
22860                     this.markInvalid(this.disabledDaysText);
22861                     return false;
22862                 }
22863             }
22864         }
22865         var fvalue = this.formatDate(value);
22866         if(this.ddMatch && this.ddMatch.test(fvalue)){
22867             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22868             return false;
22869         }
22870         return true;
22871     },
22872
22873     // private
22874     // Provides logic to override the default TriggerField.validateBlur which just returns true
22875     validateBlur : function(){
22876         return !this.menu || !this.menu.isVisible();
22877     },
22878     
22879     getName: function()
22880     {
22881         // returns hidden if it's set..
22882         if (!this.rendered) {return ''};
22883         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
22884         
22885     },
22886
22887     /**
22888      * Returns the current date value of the date field.
22889      * @return {Date} The date value
22890      */
22891     getValue : function(){
22892         
22893         return  this.hiddenField ?
22894                 this.hiddenField.value :
22895                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22896     },
22897
22898     /**
22899      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22900      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22901      * (the default format used is "m/d/y").
22902      * <br />Usage:
22903      * <pre><code>
22904 //All of these calls set the same date value (May 4, 2006)
22905
22906 //Pass a date object:
22907 var dt = new Date('5/4/06');
22908 dateField.setValue(dt);
22909
22910 //Pass a date string (default format):
22911 dateField.setValue('5/4/06');
22912
22913 //Pass a date string (custom format):
22914 dateField.format = 'Y-m-d';
22915 dateField.setValue('2006-5-4');
22916 </code></pre>
22917      * @param {String/Date} date The date or valid date string
22918      */
22919     setValue : function(date){
22920         if (this.hiddenField) {
22921             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22922         }
22923         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22924         // make sure the value field is always stored as a date..
22925         this.value = this.parseDate(date);
22926         
22927         
22928     },
22929
22930     // private
22931     parseDate : function(value){
22932         if(!value || value instanceof Date){
22933             return value;
22934         }
22935         var v = Date.parseDate(value, this.format);
22936          if (!v && this.useIso) {
22937             v = Date.parseDate(value, 'Y-m-d');
22938         }
22939         if(!v && this.altFormats){
22940             if(!this.altFormatsArray){
22941                 this.altFormatsArray = this.altFormats.split("|");
22942             }
22943             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22944                 v = Date.parseDate(value, this.altFormatsArray[i]);
22945             }
22946         }
22947         return v;
22948     },
22949
22950     // private
22951     formatDate : function(date, fmt){
22952         return (!date || !(date instanceof Date)) ?
22953                date : date.dateFormat(fmt || this.format);
22954     },
22955
22956     // private
22957     menuListeners : {
22958         select: function(m, d){
22959             
22960             this.setValue(d);
22961             this.fireEvent('select', this, d);
22962         },
22963         show : function(){ // retain focus styling
22964             this.onFocus();
22965         },
22966         hide : function(){
22967             this.focus.defer(10, this);
22968             var ml = this.menuListeners;
22969             this.menu.un("select", ml.select,  this);
22970             this.menu.un("show", ml.show,  this);
22971             this.menu.un("hide", ml.hide,  this);
22972         }
22973     },
22974
22975     // private
22976     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22977     onTriggerClick : function(){
22978         if(this.disabled){
22979             return;
22980         }
22981         if(this.menu == null){
22982             this.menu = new Roo.menu.DateMenu();
22983         }
22984         Roo.apply(this.menu.picker,  {
22985             showClear: this.allowBlank,
22986             minDate : this.minValue,
22987             maxDate : this.maxValue,
22988             disabledDatesRE : this.ddMatch,
22989             disabledDatesText : this.disabledDatesText,
22990             disabledDays : this.disabledDays,
22991             disabledDaysText : this.disabledDaysText,
22992             format : this.useIso ? 'Y-m-d' : this.format,
22993             minText : String.format(this.minText, this.formatDate(this.minValue)),
22994             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22995         });
22996         this.menu.on(Roo.apply({}, this.menuListeners, {
22997             scope:this
22998         }));
22999         this.menu.picker.setValue(this.getValue() || new Date());
23000         this.menu.show(this.el, "tl-bl?");
23001     },
23002
23003     beforeBlur : function(){
23004         var v = this.parseDate(this.getRawValue());
23005         if(v){
23006             this.setValue(v);
23007         }
23008     }
23009
23010     /** @cfg {Boolean} grow @hide */
23011     /** @cfg {Number} growMin @hide */
23012     /** @cfg {Number} growMax @hide */
23013     /**
23014      * @hide
23015      * @method autoSize
23016      */
23017 });/*
23018  * Based on:
23019  * Ext JS Library 1.1.1
23020  * Copyright(c) 2006-2007, Ext JS, LLC.
23021  *
23022  * Originally Released Under LGPL - original licence link has changed is not relivant.
23023  *
23024  * Fork - LGPL
23025  * <script type="text/javascript">
23026  */
23027  
23028 /**
23029  * @class Roo.form.MonthField
23030  * @extends Roo.form.TriggerField
23031  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
23032 * @constructor
23033 * Create a new MonthField
23034 * @param {Object} config
23035  */
23036 Roo.form.MonthField = function(config){
23037     
23038     Roo.form.MonthField.superclass.constructor.call(this, config);
23039     
23040       this.addEvents({
23041          
23042         /**
23043          * @event select
23044          * Fires when a date is selected
23045              * @param {Roo.form.MonthFieeld} combo This combo box
23046              * @param {Date} date The date selected
23047              */
23048         'select' : true
23049          
23050     });
23051     
23052     
23053     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
23054     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
23055     this.ddMatch = null;
23056     if(this.disabledDates){
23057         var dd = this.disabledDates;
23058         var re = "(?:";
23059         for(var i = 0; i < dd.length; i++){
23060             re += dd[i];
23061             if(i != dd.length-1) re += "|";
23062         }
23063         this.ddMatch = new RegExp(re + ")");
23064     }
23065 };
23066
23067 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
23068     /**
23069      * @cfg {String} format
23070      * The default date format string which can be overriden for localization support.  The format must be
23071      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
23072      */
23073     format : "M Y",
23074     /**
23075      * @cfg {String} altFormats
23076      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
23077      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
23078      */
23079     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
23080     /**
23081      * @cfg {Array} disabledDays
23082      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
23083      */
23084     disabledDays : [0,1,2,3,4,5,6],
23085     /**
23086      * @cfg {String} disabledDaysText
23087      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
23088      */
23089     disabledDaysText : "Disabled",
23090     /**
23091      * @cfg {Array} disabledDates
23092      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
23093      * expression so they are very powerful. Some examples:
23094      * <ul>
23095      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
23096      * <li>["03/08", "09/16"] would disable those days for every year</li>
23097      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
23098      * <li>["03/../2006"] would disable every day in March 2006</li>
23099      * <li>["^03"] would disable every day in every March</li>
23100      * </ul>
23101      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
23102      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
23103      */
23104     disabledDates : null,
23105     /**
23106      * @cfg {String} disabledDatesText
23107      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
23108      */
23109     disabledDatesText : "Disabled",
23110     /**
23111      * @cfg {Date/String} minValue
23112      * The minimum allowed date. Can be either a Javascript date object or a string date in a
23113      * valid format (defaults to null).
23114      */
23115     minValue : null,
23116     /**
23117      * @cfg {Date/String} maxValue
23118      * The maximum allowed date. Can be either a Javascript date object or a string date in a
23119      * valid format (defaults to null).
23120      */
23121     maxValue : null,
23122     /**
23123      * @cfg {String} minText
23124      * The error text to display when the date in the cell is before minValue (defaults to
23125      * 'The date in this field must be after {minValue}').
23126      */
23127     minText : "The date in this field must be equal to or after {0}",
23128     /**
23129      * @cfg {String} maxTextf
23130      * The error text to display when the date in the cell is after maxValue (defaults to
23131      * 'The date in this field must be before {maxValue}').
23132      */
23133     maxText : "The date in this field must be equal to or before {0}",
23134     /**
23135      * @cfg {String} invalidText
23136      * The error text to display when the date in the field is invalid (defaults to
23137      * '{value} is not a valid date - it must be in the format {format}').
23138      */
23139     invalidText : "{0} is not a valid date - it must be in the format {1}",
23140     /**
23141      * @cfg {String} triggerClass
23142      * An additional CSS class used to style the trigger button.  The trigger will always get the
23143      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
23144      * which displays a calendar icon).
23145      */
23146     triggerClass : 'x-form-date-trigger',
23147     
23148
23149     /**
23150      * @cfg {Boolean} useIso
23151      * if enabled, then the date field will use a hidden field to store the 
23152      * real value as iso formated date. default (true)
23153      */ 
23154     useIso : true,
23155     /**
23156      * @cfg {String/Object} autoCreate
23157      * A DomHelper element spec, or true for a default element spec (defaults to
23158      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
23159      */ 
23160     // private
23161     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
23162     
23163     // private
23164     hiddenField: false,
23165     
23166     hideMonthPicker : false,
23167     
23168     onRender : function(ct, position)
23169     {
23170         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
23171         if (this.useIso) {
23172             this.el.dom.removeAttribute('name'); 
23173             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
23174                     'before', true);
23175             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
23176             // prevent input submission
23177             this.hiddenName = this.name;
23178         }
23179             
23180             
23181     },
23182     
23183     // private
23184     validateValue : function(value)
23185     {
23186         value = this.formatDate(value);
23187         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
23188             return false;
23189         }
23190         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
23191              return true;
23192         }
23193         var svalue = value;
23194         value = this.parseDate(value);
23195         if(!value){
23196             this.markInvalid(String.format(this.invalidText, svalue, this.format));
23197             return false;
23198         }
23199         var time = value.getTime();
23200         if(this.minValue && time < this.minValue.getTime()){
23201             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
23202             return false;
23203         }
23204         if(this.maxValue && time > this.maxValue.getTime()){
23205             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
23206             return false;
23207         }
23208         /*if(this.disabledDays){
23209             var day = value.getDay();
23210             for(var i = 0; i < this.disabledDays.length; i++) {
23211                 if(day === this.disabledDays[i]){
23212                     this.markInvalid(this.disabledDaysText);
23213                     return false;
23214                 }
23215             }
23216         }
23217         */
23218         var fvalue = this.formatDate(value);
23219         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
23220             this.markInvalid(String.format(this.disabledDatesText, fvalue));
23221             return false;
23222         }
23223         */
23224         return true;
23225     },
23226
23227     // private
23228     // Provides logic to override the default TriggerField.validateBlur which just returns true
23229     validateBlur : function(){
23230         return !this.menu || !this.menu.isVisible();
23231     },
23232
23233     /**
23234      * Returns the current date value of the date field.
23235      * @return {Date} The date value
23236      */
23237     getValue : function(){
23238         
23239         
23240         
23241         return  this.hiddenField ?
23242                 this.hiddenField.value :
23243                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
23244     },
23245
23246     /**
23247      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
23248      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
23249      * (the default format used is "m/d/y").
23250      * <br />Usage:
23251      * <pre><code>
23252 //All of these calls set the same date value (May 4, 2006)
23253
23254 //Pass a date object:
23255 var dt = new Date('5/4/06');
23256 monthField.setValue(dt);
23257
23258 //Pass a date string (default format):
23259 monthField.setValue('5/4/06');
23260
23261 //Pass a date string (custom format):
23262 monthField.format = 'Y-m-d';
23263 monthField.setValue('2006-5-4');
23264 </code></pre>
23265      * @param {String/Date} date The date or valid date string
23266      */
23267     setValue : function(date){
23268         Roo.log('month setValue' + date);
23269         // can only be first of month..
23270         
23271         var val = this.parseDate(date);
23272         
23273         if (this.hiddenField) {
23274             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
23275         }
23276         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
23277         this.value = this.parseDate(date);
23278     },
23279
23280     // private
23281     parseDate : function(value){
23282         if(!value || value instanceof Date){
23283             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
23284             return value;
23285         }
23286         var v = Date.parseDate(value, this.format);
23287         if (!v && this.useIso) {
23288             v = Date.parseDate(value, 'Y-m-d');
23289         }
23290         if (v) {
23291             // 
23292             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
23293         }
23294         
23295         
23296         if(!v && this.altFormats){
23297             if(!this.altFormatsArray){
23298                 this.altFormatsArray = this.altFormats.split("|");
23299             }
23300             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23301                 v = Date.parseDate(value, this.altFormatsArray[i]);
23302             }
23303         }
23304         return v;
23305     },
23306
23307     // private
23308     formatDate : function(date, fmt){
23309         return (!date || !(date instanceof Date)) ?
23310                date : date.dateFormat(fmt || this.format);
23311     },
23312
23313     // private
23314     menuListeners : {
23315         select: function(m, d){
23316             this.setValue(d);
23317             this.fireEvent('select', this, d);
23318         },
23319         show : function(){ // retain focus styling
23320             this.onFocus();
23321         },
23322         hide : function(){
23323             this.focus.defer(10, this);
23324             var ml = this.menuListeners;
23325             this.menu.un("select", ml.select,  this);
23326             this.menu.un("show", ml.show,  this);
23327             this.menu.un("hide", ml.hide,  this);
23328         }
23329     },
23330     // private
23331     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
23332     onTriggerClick : function(){
23333         if(this.disabled){
23334             return;
23335         }
23336         if(this.menu == null){
23337             this.menu = new Roo.menu.DateMenu();
23338            
23339         }
23340         
23341         Roo.apply(this.menu.picker,  {
23342             
23343             showClear: this.allowBlank,
23344             minDate : this.minValue,
23345             maxDate : this.maxValue,
23346             disabledDatesRE : this.ddMatch,
23347             disabledDatesText : this.disabledDatesText,
23348             
23349             format : this.useIso ? 'Y-m-d' : this.format,
23350             minText : String.format(this.minText, this.formatDate(this.minValue)),
23351             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
23352             
23353         });
23354          this.menu.on(Roo.apply({}, this.menuListeners, {
23355             scope:this
23356         }));
23357        
23358         
23359         var m = this.menu;
23360         var p = m.picker;
23361         
23362         // hide month picker get's called when we called by 'before hide';
23363         
23364         var ignorehide = true;
23365         p.hideMonthPicker  = function(disableAnim){
23366             if (ignorehide) {
23367                 return;
23368             }
23369              if(this.monthPicker){
23370                 Roo.log("hideMonthPicker called");
23371                 if(disableAnim === true){
23372                     this.monthPicker.hide();
23373                 }else{
23374                     this.monthPicker.slideOut('t', {duration:.2});
23375                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
23376                     p.fireEvent("select", this, this.value);
23377                     m.hide();
23378                 }
23379             }
23380         }
23381         
23382         Roo.log('picker set value');
23383         Roo.log(this.getValue());
23384         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
23385         m.show(this.el, 'tl-bl?');
23386         ignorehide  = false;
23387         // this will trigger hideMonthPicker..
23388         
23389         
23390         // hidden the day picker
23391         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
23392         
23393         
23394         
23395       
23396         
23397         p.showMonthPicker.defer(100, p);
23398     
23399         
23400        
23401     },
23402
23403     beforeBlur : function(){
23404         var v = this.parseDate(this.getRawValue());
23405         if(v){
23406             this.setValue(v);
23407         }
23408     }
23409
23410     /** @cfg {Boolean} grow @hide */
23411     /** @cfg {Number} growMin @hide */
23412     /** @cfg {Number} growMax @hide */
23413     /**
23414      * @hide
23415      * @method autoSize
23416      */
23417 });/*
23418  * Based on:
23419  * Ext JS Library 1.1.1
23420  * Copyright(c) 2006-2007, Ext JS, LLC.
23421  *
23422  * Originally Released Under LGPL - original licence link has changed is not relivant.
23423  *
23424  * Fork - LGPL
23425  * <script type="text/javascript">
23426  */
23427  
23428
23429 /**
23430  * @class Roo.form.ComboBox
23431  * @extends Roo.form.TriggerField
23432  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
23433  * @constructor
23434  * Create a new ComboBox.
23435  * @param {Object} config Configuration options
23436  */
23437 Roo.form.ComboBox = function(config){
23438     Roo.form.ComboBox.superclass.constructor.call(this, config);
23439     this.addEvents({
23440         /**
23441          * @event expand
23442          * Fires when the dropdown list is expanded
23443              * @param {Roo.form.ComboBox} combo This combo box
23444              */
23445         'expand' : true,
23446         /**
23447          * @event collapse
23448          * Fires when the dropdown list is collapsed
23449              * @param {Roo.form.ComboBox} combo This combo box
23450              */
23451         'collapse' : true,
23452         /**
23453          * @event beforeselect
23454          * Fires before a list item is selected. Return false to cancel the selection.
23455              * @param {Roo.form.ComboBox} combo This combo box
23456              * @param {Roo.data.Record} record The data record returned from the underlying store
23457              * @param {Number} index The index of the selected item in the dropdown list
23458              */
23459         'beforeselect' : true,
23460         /**
23461          * @event select
23462          * Fires when a list item is selected
23463              * @param {Roo.form.ComboBox} combo This combo box
23464              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
23465              * @param {Number} index The index of the selected item in the dropdown list
23466              */
23467         'select' : true,
23468         /**
23469          * @event beforequery
23470          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
23471          * The event object passed has these properties:
23472              * @param {Roo.form.ComboBox} combo This combo box
23473              * @param {String} query The query
23474              * @param {Boolean} forceAll true to force "all" query
23475              * @param {Boolean} cancel true to cancel the query
23476              * @param {Object} e The query event object
23477              */
23478         'beforequery': true,
23479          /**
23480          * @event add
23481          * Fires when the 'add' icon is pressed (add a listener to enable add button)
23482              * @param {Roo.form.ComboBox} combo This combo box
23483              */
23484         'add' : true,
23485         /**
23486          * @event edit
23487          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
23488              * @param {Roo.form.ComboBox} combo This combo box
23489              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
23490              */
23491         'edit' : true
23492         
23493         
23494     });
23495     if(this.transform){
23496         this.allowDomMove = false;
23497         var s = Roo.getDom(this.transform);
23498         if(!this.hiddenName){
23499             this.hiddenName = s.name;
23500         }
23501         if(!this.store){
23502             this.mode = 'local';
23503             var d = [], opts = s.options;
23504             for(var i = 0, len = opts.length;i < len; i++){
23505                 var o = opts[i];
23506                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
23507                 if(o.selected) {
23508                     this.value = value;
23509                 }
23510                 d.push([value, o.text]);
23511             }
23512             this.store = new Roo.data.SimpleStore({
23513                 'id': 0,
23514                 fields: ['value', 'text'],
23515                 data : d
23516             });
23517             this.valueField = 'value';
23518             this.displayField = 'text';
23519         }
23520         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
23521         if(!this.lazyRender){
23522             this.target = true;
23523             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
23524             s.parentNode.removeChild(s); // remove it
23525             this.render(this.el.parentNode);
23526         }else{
23527             s.parentNode.removeChild(s); // remove it
23528         }
23529
23530     }
23531     if (this.store) {
23532         this.store = Roo.factory(this.store, Roo.data);
23533     }
23534     
23535     this.selectedIndex = -1;
23536     if(this.mode == 'local'){
23537         if(config.queryDelay === undefined){
23538             this.queryDelay = 10;
23539         }
23540         if(config.minChars === undefined){
23541             this.minChars = 0;
23542         }
23543     }
23544 };
23545
23546 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
23547     /**
23548      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
23549      */
23550     /**
23551      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
23552      * rendering into an Roo.Editor, defaults to false)
23553      */
23554     /**
23555      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
23556      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
23557      */
23558     /**
23559      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
23560      */
23561     /**
23562      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
23563      * the dropdown list (defaults to undefined, with no header element)
23564      */
23565
23566      /**
23567      * @cfg {String/Roo.Template} tpl The template to use to render the output
23568      */
23569      
23570     // private
23571     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
23572     /**
23573      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
23574      */
23575     listWidth: undefined,
23576     /**
23577      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
23578      * mode = 'remote' or 'text' if mode = 'local')
23579      */
23580     displayField: undefined,
23581     /**
23582      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
23583      * mode = 'remote' or 'value' if mode = 'local'). 
23584      * Note: use of a valueField requires the user make a selection
23585      * in order for a value to be mapped.
23586      */
23587     valueField: undefined,
23588     
23589     
23590     /**
23591      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
23592      * field's data value (defaults to the underlying DOM element's name)
23593      */
23594     hiddenName: undefined,
23595     /**
23596      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
23597      */
23598     listClass: '',
23599     /**
23600      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
23601      */
23602     selectedClass: 'x-combo-selected',
23603     /**
23604      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
23605      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
23606      * which displays a downward arrow icon).
23607      */
23608     triggerClass : 'x-form-arrow-trigger',
23609     /**
23610      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
23611      */
23612     shadow:'sides',
23613     /**
23614      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
23615      * anchor positions (defaults to 'tl-bl')
23616      */
23617     listAlign: 'tl-bl?',
23618     /**
23619      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
23620      */
23621     maxHeight: 300,
23622     /**
23623      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
23624      * query specified by the allQuery config option (defaults to 'query')
23625      */
23626     triggerAction: 'query',
23627     /**
23628      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
23629      * (defaults to 4, does not apply if editable = false)
23630      */
23631     minChars : 4,
23632     /**
23633      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
23634      * delay (typeAheadDelay) if it matches a known value (defaults to false)
23635      */
23636     typeAhead: false,
23637     /**
23638      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
23639      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
23640      */
23641     queryDelay: 500,
23642     /**
23643      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
23644      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
23645      */
23646     pageSize: 0,
23647     /**
23648      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
23649      * when editable = true (defaults to false)
23650      */
23651     selectOnFocus:false,
23652     /**
23653      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
23654      */
23655     queryParam: 'query',
23656     /**
23657      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
23658      * when mode = 'remote' (defaults to 'Loading...')
23659      */
23660     loadingText: 'Loading...',
23661     /**
23662      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
23663      */
23664     resizable: false,
23665     /**
23666      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
23667      */
23668     handleHeight : 8,
23669     /**
23670      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
23671      * traditional select (defaults to true)
23672      */
23673     editable: true,
23674     /**
23675      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
23676      */
23677     allQuery: '',
23678     /**
23679      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
23680      */
23681     mode: 'remote',
23682     /**
23683      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
23684      * listWidth has a higher value)
23685      */
23686     minListWidth : 70,
23687     /**
23688      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
23689      * allow the user to set arbitrary text into the field (defaults to false)
23690      */
23691     forceSelection:false,
23692     /**
23693      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
23694      * if typeAhead = true (defaults to 250)
23695      */
23696     typeAheadDelay : 250,
23697     /**
23698      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
23699      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
23700      */
23701     valueNotFoundText : undefined,
23702     /**
23703      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
23704      */
23705     blockFocus : false,
23706     
23707     /**
23708      * @cfg {Boolean} disableClear Disable showing of clear button.
23709      */
23710     disableClear : false,
23711     /**
23712      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
23713      */
23714     alwaysQuery : false,
23715     
23716     //private
23717     addicon : false,
23718     editicon: false,
23719     
23720     // element that contains real text value.. (when hidden is used..)
23721      
23722     // private
23723     onRender : function(ct, position){
23724         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
23725         if(this.hiddenName){
23726             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
23727                     'before', true);
23728             this.hiddenField.value =
23729                 this.hiddenValue !== undefined ? this.hiddenValue :
23730                 this.value !== undefined ? this.value : '';
23731
23732             // prevent input submission
23733             this.el.dom.removeAttribute('name');
23734              
23735              
23736         }
23737         if(Roo.isGecko){
23738             this.el.dom.setAttribute('autocomplete', 'off');
23739         }
23740
23741         var cls = 'x-combo-list';
23742
23743         this.list = new Roo.Layer({
23744             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
23745         });
23746
23747         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
23748         this.list.setWidth(lw);
23749         this.list.swallowEvent('mousewheel');
23750         this.assetHeight = 0;
23751
23752         if(this.title){
23753             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
23754             this.assetHeight += this.header.getHeight();
23755         }
23756
23757         this.innerList = this.list.createChild({cls:cls+'-inner'});
23758         this.innerList.on('mouseover', this.onViewOver, this);
23759         this.innerList.on('mousemove', this.onViewMove, this);
23760         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23761         
23762         if(this.allowBlank && !this.pageSize && !this.disableClear){
23763             this.footer = this.list.createChild({cls:cls+'-ft'});
23764             this.pageTb = new Roo.Toolbar(this.footer);
23765            
23766         }
23767         if(this.pageSize){
23768             this.footer = this.list.createChild({cls:cls+'-ft'});
23769             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
23770                     {pageSize: this.pageSize});
23771             
23772         }
23773         
23774         if (this.pageTb && this.allowBlank && !this.disableClear) {
23775             var _this = this;
23776             this.pageTb.add(new Roo.Toolbar.Fill(), {
23777                 cls: 'x-btn-icon x-btn-clear',
23778                 text: '&#160;',
23779                 handler: function()
23780                 {
23781                     _this.collapse();
23782                     _this.clearValue();
23783                     _this.onSelect(false, -1);
23784                 }
23785             });
23786         }
23787         if (this.footer) {
23788             this.assetHeight += this.footer.getHeight();
23789         }
23790         
23791
23792         if(!this.tpl){
23793             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
23794         }
23795
23796         this.view = new Roo.View(this.innerList, this.tpl, {
23797             singleSelect:true, store: this.store, selectedClass: this.selectedClass
23798         });
23799
23800         this.view.on('click', this.onViewClick, this);
23801
23802         this.store.on('beforeload', this.onBeforeLoad, this);
23803         this.store.on('load', this.onLoad, this);
23804         this.store.on('loadexception', this.onLoadException, this);
23805
23806         if(this.resizable){
23807             this.resizer = new Roo.Resizable(this.list,  {
23808                pinned:true, handles:'se'
23809             });
23810             this.resizer.on('resize', function(r, w, h){
23811                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
23812                 this.listWidth = w;
23813                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
23814                 this.restrictHeight();
23815             }, this);
23816             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
23817         }
23818         if(!this.editable){
23819             this.editable = true;
23820             this.setEditable(false);
23821         }  
23822         
23823         
23824         if (typeof(this.events.add.listeners) != 'undefined') {
23825             
23826             this.addicon = this.wrap.createChild(
23827                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
23828        
23829             this.addicon.on('click', function(e) {
23830                 this.fireEvent('add', this);
23831             }, this);
23832         }
23833         if (typeof(this.events.edit.listeners) != 'undefined') {
23834             
23835             this.editicon = this.wrap.createChild(
23836                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
23837             if (this.addicon) {
23838                 this.editicon.setStyle('margin-left', '40px');
23839             }
23840             this.editicon.on('click', function(e) {
23841                 
23842                 // we fire even  if inothing is selected..
23843                 this.fireEvent('edit', this, this.lastData );
23844                 
23845             }, this);
23846         }
23847         
23848         
23849         
23850     },
23851
23852     // private
23853     initEvents : function(){
23854         Roo.form.ComboBox.superclass.initEvents.call(this);
23855
23856         this.keyNav = new Roo.KeyNav(this.el, {
23857             "up" : function(e){
23858                 this.inKeyMode = true;
23859                 this.selectPrev();
23860             },
23861
23862             "down" : function(e){
23863                 if(!this.isExpanded()){
23864                     this.onTriggerClick();
23865                 }else{
23866                     this.inKeyMode = true;
23867                     this.selectNext();
23868                 }
23869             },
23870
23871             "enter" : function(e){
23872                 this.onViewClick();
23873                 //return true;
23874             },
23875
23876             "esc" : function(e){
23877                 this.collapse();
23878             },
23879
23880             "tab" : function(e){
23881                 this.onViewClick(false);
23882                 this.fireEvent("specialkey", this, e);
23883                 return true;
23884             },
23885
23886             scope : this,
23887
23888             doRelay : function(foo, bar, hname){
23889                 if(hname == 'down' || this.scope.isExpanded()){
23890                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
23891                 }
23892                 return true;
23893             },
23894
23895             forceKeyDown: true
23896         });
23897         this.queryDelay = Math.max(this.queryDelay || 10,
23898                 this.mode == 'local' ? 10 : 250);
23899         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
23900         if(this.typeAhead){
23901             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
23902         }
23903         if(this.editable !== false){
23904             this.el.on("keyup", this.onKeyUp, this);
23905         }
23906         if(this.forceSelection){
23907             this.on('blur', this.doForce, this);
23908         }
23909     },
23910
23911     onDestroy : function(){
23912         if(this.view){
23913             this.view.setStore(null);
23914             this.view.el.removeAllListeners();
23915             this.view.el.remove();
23916             this.view.purgeListeners();
23917         }
23918         if(this.list){
23919             this.list.destroy();
23920         }
23921         if(this.store){
23922             this.store.un('beforeload', this.onBeforeLoad, this);
23923             this.store.un('load', this.onLoad, this);
23924             this.store.un('loadexception', this.onLoadException, this);
23925         }
23926         Roo.form.ComboBox.superclass.onDestroy.call(this);
23927     },
23928
23929     // private
23930     fireKey : function(e){
23931         if(e.isNavKeyPress() && !this.list.isVisible()){
23932             this.fireEvent("specialkey", this, e);
23933         }
23934     },
23935
23936     // private
23937     onResize: function(w, h){
23938         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
23939         
23940         if(typeof w != 'number'){
23941             // we do not handle it!?!?
23942             return;
23943         }
23944         var tw = this.trigger.getWidth();
23945         tw += this.addicon ? this.addicon.getWidth() : 0;
23946         tw += this.editicon ? this.editicon.getWidth() : 0;
23947         var x = w - tw;
23948         this.el.setWidth( this.adjustWidth('input', x));
23949             
23950         this.trigger.setStyle('left', x+'px');
23951         
23952         if(this.list && this.listWidth === undefined){
23953             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
23954             this.list.setWidth(lw);
23955             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23956         }
23957         
23958     
23959         
23960     },
23961
23962     /**
23963      * Allow or prevent the user from directly editing the field text.  If false is passed,
23964      * the user will only be able to select from the items defined in the dropdown list.  This method
23965      * is the runtime equivalent of setting the 'editable' config option at config time.
23966      * @param {Boolean} value True to allow the user to directly edit the field text
23967      */
23968     setEditable : function(value){
23969         if(value == this.editable){
23970             return;
23971         }
23972         this.editable = value;
23973         if(!value){
23974             this.el.dom.setAttribute('readOnly', true);
23975             this.el.on('mousedown', this.onTriggerClick,  this);
23976             this.el.addClass('x-combo-noedit');
23977         }else{
23978             this.el.dom.setAttribute('readOnly', false);
23979             this.el.un('mousedown', this.onTriggerClick,  this);
23980             this.el.removeClass('x-combo-noedit');
23981         }
23982     },
23983
23984     // private
23985     onBeforeLoad : function(){
23986         if(!this.hasFocus){
23987             return;
23988         }
23989         this.innerList.update(this.loadingText ?
23990                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
23991         this.restrictHeight();
23992         this.selectedIndex = -1;
23993     },
23994
23995     // private
23996     onLoad : function(){
23997         if(!this.hasFocus){
23998             return;
23999         }
24000         if(this.store.getCount() > 0){
24001             this.expand();
24002             this.restrictHeight();
24003             if(this.lastQuery == this.allQuery){
24004                 if(this.editable){
24005                     this.el.dom.select();
24006                 }
24007                 if(!this.selectByValue(this.value, true)){
24008                     this.select(0, true);
24009                 }
24010             }else{
24011                 this.selectNext();
24012                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
24013                     this.taTask.delay(this.typeAheadDelay);
24014                 }
24015             }
24016         }else{
24017             this.onEmptyResults();
24018         }
24019         //this.el.focus();
24020     },
24021     // private
24022     onLoadException : function()
24023     {
24024         this.collapse();
24025         Roo.log(this.store.reader.jsonData);
24026         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
24027             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
24028         }
24029         
24030         
24031     },
24032     // private
24033     onTypeAhead : function(){
24034         if(this.store.getCount() > 0){
24035             var r = this.store.getAt(0);
24036             var newValue = r.data[this.displayField];
24037             var len = newValue.length;
24038             var selStart = this.getRawValue().length;
24039             if(selStart != len){
24040                 this.setRawValue(newValue);
24041                 this.selectText(selStart, newValue.length);
24042             }
24043         }
24044     },
24045
24046     // private
24047     onSelect : function(record, index){
24048         if(this.fireEvent('beforeselect', this, record, index) !== false){
24049             this.setFromData(index > -1 ? record.data : false);
24050             this.collapse();
24051             this.fireEvent('select', this, record, index);
24052         }
24053     },
24054
24055     /**
24056      * Returns the currently selected field value or empty string if no value is set.
24057      * @return {String} value The selected value
24058      */
24059     getValue : function(){
24060         if(this.valueField){
24061             return typeof this.value != 'undefined' ? this.value : '';
24062         }else{
24063             return Roo.form.ComboBox.superclass.getValue.call(this);
24064         }
24065     },
24066
24067     /**
24068      * Clears any text/value currently set in the field
24069      */
24070     clearValue : function(){
24071         if(this.hiddenField){
24072             this.hiddenField.value = '';
24073         }
24074         this.value = '';
24075         this.setRawValue('');
24076         this.lastSelectionText = '';
24077         
24078     },
24079
24080     /**
24081      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
24082      * will be displayed in the field.  If the value does not match the data value of an existing item,
24083      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
24084      * Otherwise the field will be blank (although the value will still be set).
24085      * @param {String} value The value to match
24086      */
24087     setValue : function(v){
24088         var text = v;
24089         if(this.valueField){
24090             var r = this.findRecord(this.valueField, v);
24091             if(r){
24092                 text = r.data[this.displayField];
24093             }else if(this.valueNotFoundText !== undefined){
24094                 text = this.valueNotFoundText;
24095             }
24096         }
24097         this.lastSelectionText = text;
24098         if(this.hiddenField){
24099             this.hiddenField.value = v;
24100         }
24101         Roo.form.ComboBox.superclass.setValue.call(this, text);
24102         this.value = v;
24103     },
24104     /**
24105      * @property {Object} the last set data for the element
24106      */
24107     
24108     lastData : false,
24109     /**
24110      * Sets the value of the field based on a object which is related to the record format for the store.
24111      * @param {Object} value the value to set as. or false on reset?
24112      */
24113     setFromData : function(o){
24114         var dv = ''; // display value
24115         var vv = ''; // value value..
24116         this.lastData = o;
24117         if (this.displayField) {
24118             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
24119         } else {
24120             // this is an error condition!!!
24121             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
24122         }
24123         
24124         if(this.valueField){
24125             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
24126         }
24127         if(this.hiddenField){
24128             this.hiddenField.value = vv;
24129             
24130             this.lastSelectionText = dv;
24131             Roo.form.ComboBox.superclass.setValue.call(this, dv);
24132             this.value = vv;
24133             return;
24134         }
24135         // no hidden field.. - we store the value in 'value', but still display
24136         // display field!!!!
24137         this.lastSelectionText = dv;
24138         Roo.form.ComboBox.superclass.setValue.call(this, dv);
24139         this.value = vv;
24140         
24141         
24142     },
24143     // private
24144     reset : function(){
24145         // overridden so that last data is reset..
24146         this.setValue(this.originalValue);
24147         this.clearInvalid();
24148         this.lastData = false;
24149         if (this.view) {
24150             this.view.clearSelections();
24151         }
24152     },
24153     // private
24154     findRecord : function(prop, value){
24155         var record;
24156         if(this.store.getCount() > 0){
24157             this.store.each(function(r){
24158                 if(r.data[prop] == value){
24159                     record = r;
24160                     return false;
24161                 }
24162                 return true;
24163             });
24164         }
24165         return record;
24166     },
24167     
24168     getName: function()
24169     {
24170         // returns hidden if it's set..
24171         if (!this.rendered) {return ''};
24172         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
24173         
24174     },
24175     // private
24176     onViewMove : function(e, t){
24177         this.inKeyMode = false;
24178     },
24179
24180     // private
24181     onViewOver : function(e, t){
24182         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
24183             return;
24184         }
24185         var item = this.view.findItemFromChild(t);
24186         if(item){
24187             var index = this.view.indexOf(item);
24188             this.select(index, false);
24189         }
24190     },
24191
24192     // private
24193     onViewClick : function(doFocus)
24194     {
24195         var index = this.view.getSelectedIndexes()[0];
24196         var r = this.store.getAt(index);
24197         if(r){
24198             this.onSelect(r, index);
24199         }
24200         if(doFocus !== false && !this.blockFocus){
24201             this.el.focus();
24202         }
24203     },
24204
24205     // private
24206     restrictHeight : function(){
24207         this.innerList.dom.style.height = '';
24208         var inner = this.innerList.dom;
24209         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
24210         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
24211         this.list.beginUpdate();
24212         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
24213         this.list.alignTo(this.el, this.listAlign);
24214         this.list.endUpdate();
24215     },
24216
24217     // private
24218     onEmptyResults : function(){
24219         this.collapse();
24220     },
24221
24222     /**
24223      * Returns true if the dropdown list is expanded, else false.
24224      */
24225     isExpanded : function(){
24226         return this.list.isVisible();
24227     },
24228
24229     /**
24230      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
24231      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
24232      * @param {String} value The data value of the item to select
24233      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
24234      * selected item if it is not currently in view (defaults to true)
24235      * @return {Boolean} True if the value matched an item in the list, else false
24236      */
24237     selectByValue : function(v, scrollIntoView){
24238         if(v !== undefined && v !== null){
24239             var r = this.findRecord(this.valueField || this.displayField, v);
24240             if(r){
24241                 this.select(this.store.indexOf(r), scrollIntoView);
24242                 return true;
24243             }
24244         }
24245         return false;
24246     },
24247
24248     /**
24249      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
24250      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
24251      * @param {Number} index The zero-based index of the list item to select
24252      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
24253      * selected item if it is not currently in view (defaults to true)
24254      */
24255     select : function(index, scrollIntoView){
24256         this.selectedIndex = index;
24257         this.view.select(index);
24258         if(scrollIntoView !== false){
24259             var el = this.view.getNode(index);
24260             if(el){
24261                 this.innerList.scrollChildIntoView(el, false);
24262             }
24263         }
24264     },
24265
24266     // private
24267     selectNext : function(){
24268         var ct = this.store.getCount();
24269         if(ct > 0){
24270             if(this.selectedIndex == -1){
24271                 this.select(0);
24272             }else if(this.selectedIndex < ct-1){
24273                 this.select(this.selectedIndex+1);
24274             }
24275         }
24276     },
24277
24278     // private
24279     selectPrev : function(){
24280         var ct = this.store.getCount();
24281         if(ct > 0){
24282             if(this.selectedIndex == -1){
24283                 this.select(0);
24284             }else if(this.selectedIndex != 0){
24285                 this.select(this.selectedIndex-1);
24286             }
24287         }
24288     },
24289
24290     // private
24291     onKeyUp : function(e){
24292         if(this.editable !== false && !e.isSpecialKey()){
24293             this.lastKey = e.getKey();
24294             this.dqTask.delay(this.queryDelay);
24295         }
24296     },
24297
24298     // private
24299     validateBlur : function(){
24300         return !this.list || !this.list.isVisible();   
24301     },
24302
24303     // private
24304     initQuery : function(){
24305         this.doQuery(this.getRawValue());
24306     },
24307
24308     // private
24309     doForce : function(){
24310         if(this.el.dom.value.length > 0){
24311             this.el.dom.value =
24312                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
24313              
24314         }
24315     },
24316
24317     /**
24318      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
24319      * query allowing the query action to be canceled if needed.
24320      * @param {String} query The SQL query to execute
24321      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
24322      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
24323      * saved in the current store (defaults to false)
24324      */
24325     doQuery : function(q, forceAll){
24326         if(q === undefined || q === null){
24327             q = '';
24328         }
24329         var qe = {
24330             query: q,
24331             forceAll: forceAll,
24332             combo: this,
24333             cancel:false
24334         };
24335         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
24336             return false;
24337         }
24338         q = qe.query;
24339         forceAll = qe.forceAll;
24340         if(forceAll === true || (q.length >= this.minChars)){
24341             if(this.lastQuery != q || this.alwaysQuery){
24342                 this.lastQuery = q;
24343                 if(this.mode == 'local'){
24344                     this.selectedIndex = -1;
24345                     if(forceAll){
24346                         this.store.clearFilter();
24347                     }else{
24348                         this.store.filter(this.displayField, q);
24349                     }
24350                     this.onLoad();
24351                 }else{
24352                     this.store.baseParams[this.queryParam] = q;
24353                     this.store.load({
24354                         params: this.getParams(q)
24355                     });
24356                     this.expand();
24357                 }
24358             }else{
24359                 this.selectedIndex = -1;
24360                 this.onLoad();   
24361             }
24362         }
24363     },
24364
24365     // private
24366     getParams : function(q){
24367         var p = {};
24368         //p[this.queryParam] = q;
24369         if(this.pageSize){
24370             p.start = 0;
24371             p.limit = this.pageSize;
24372         }
24373         return p;
24374     },
24375
24376     /**
24377      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
24378      */
24379     collapse : function(){
24380         if(!this.isExpanded()){
24381             return;
24382         }
24383         this.list.hide();
24384         Roo.get(document).un('mousedown', this.collapseIf, this);
24385         Roo.get(document).un('mousewheel', this.collapseIf, this);
24386         if (!this.editable) {
24387             Roo.get(document).un('keydown', this.listKeyPress, this);
24388         }
24389         this.fireEvent('collapse', this);
24390     },
24391
24392     // private
24393     collapseIf : function(e){
24394         if(!e.within(this.wrap) && !e.within(this.list)){
24395             this.collapse();
24396         }
24397     },
24398
24399     /**
24400      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
24401      */
24402     expand : function(){
24403         if(this.isExpanded() || !this.hasFocus){
24404             return;
24405         }
24406         this.list.alignTo(this.el, this.listAlign);
24407         this.list.show();
24408         Roo.get(document).on('mousedown', this.collapseIf, this);
24409         Roo.get(document).on('mousewheel', this.collapseIf, this);
24410         if (!this.editable) {
24411             Roo.get(document).on('keydown', this.listKeyPress, this);
24412         }
24413         
24414         this.fireEvent('expand', this);
24415     },
24416
24417     // private
24418     // Implements the default empty TriggerField.onTriggerClick function
24419     onTriggerClick : function(){
24420         if(this.disabled){
24421             return;
24422         }
24423         if(this.isExpanded()){
24424             this.collapse();
24425             if (!this.blockFocus) {
24426                 this.el.focus();
24427             }
24428             
24429         }else {
24430             this.hasFocus = true;
24431             if(this.triggerAction == 'all') {
24432                 this.doQuery(this.allQuery, true);
24433             } else {
24434                 this.doQuery(this.getRawValue());
24435             }
24436             if (!this.blockFocus) {
24437                 this.el.focus();
24438             }
24439         }
24440     },
24441     listKeyPress : function(e)
24442     {
24443         //Roo.log('listkeypress');
24444         // scroll to first matching element based on key pres..
24445         if (e.isSpecialKey()) {
24446             return false;
24447         }
24448         var k = String.fromCharCode(e.getKey()).toUpperCase();
24449         //Roo.log(k);
24450         var match  = false;
24451         var csel = this.view.getSelectedNodes();
24452         var cselitem = false;
24453         if (csel.length) {
24454             var ix = this.view.indexOf(csel[0]);
24455             cselitem  = this.store.getAt(ix);
24456             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
24457                 cselitem = false;
24458             }
24459             
24460         }
24461         
24462         this.store.each(function(v) { 
24463             if (cselitem) {
24464                 // start at existing selection.
24465                 if (cselitem.id == v.id) {
24466                     cselitem = false;
24467                 }
24468                 return;
24469             }
24470                 
24471             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
24472                 match = this.store.indexOf(v);
24473                 return false;
24474             }
24475         }, this);
24476         
24477         if (match === false) {
24478             return true; // no more action?
24479         }
24480         // scroll to?
24481         this.view.select(match);
24482         var sn = Roo.get(this.view.getSelectedNodes()[0])
24483         sn.scrollIntoView(sn.dom.parentNode, false);
24484     }
24485
24486     /** 
24487     * @cfg {Boolean} grow 
24488     * @hide 
24489     */
24490     /** 
24491     * @cfg {Number} growMin 
24492     * @hide 
24493     */
24494     /** 
24495     * @cfg {Number} growMax 
24496     * @hide 
24497     */
24498     /**
24499      * @hide
24500      * @method autoSize
24501      */
24502 });/*
24503  * Copyright(c) 2010-2012, Roo J Solutions Limited
24504  *
24505  * Licence LGPL
24506  *
24507  */
24508
24509 /**
24510  * @class Roo.form.ComboBoxArray
24511  * @extends Roo.form.TextField
24512  * A facebook style adder... for lists of email / people / countries  etc...
24513  * pick multiple items from a combo box, and shows each one.
24514  *
24515  *  Fred [x]  Brian [x]  [Pick another |v]
24516  *
24517  *
24518  *  For this to work: it needs various extra information
24519  *    - normal combo problay has
24520  *      name, hiddenName
24521  *    + displayField, valueField
24522  *
24523  *    For our purpose...
24524  *
24525  *
24526  *   If we change from 'extends' to wrapping...
24527  *   
24528  *  
24529  *
24530  
24531  
24532  * @constructor
24533  * Create a new ComboBoxArray.
24534  * @param {Object} config Configuration options
24535  */
24536  
24537
24538 Roo.form.ComboBoxArray = function(config)
24539 {
24540     
24541     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
24542     
24543     this.items = new Roo.util.MixedCollection(false);
24544     
24545     // construct the child combo...
24546     
24547     
24548     
24549     
24550    
24551     
24552 }
24553
24554  
24555 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
24556
24557     /**
24558      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
24559      */
24560     
24561     lastData : false,
24562     
24563     // behavies liek a hiddne field
24564     inputType:      'hidden',
24565     /**
24566      * @cfg {Number} width The width of the box that displays the selected element
24567      */ 
24568     width:          300,
24569
24570     
24571     
24572     /**
24573      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
24574      */
24575     name : false,
24576     /**
24577      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
24578      */
24579     hiddenName : false,
24580     
24581     
24582     // private the array of items that are displayed..
24583     items  : false,
24584     // private - the hidden field el.
24585     hiddenEl : false,
24586     // private - the filed el..
24587     el : false,
24588     
24589     //validateValue : function() { return true; }, // all values are ok!
24590     //onAddClick: function() { },
24591     
24592     onRender : function(ct, position) 
24593     {
24594         
24595         // create the standard hidden element
24596         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
24597         
24598         
24599         // give fake names to child combo;
24600         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
24601         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
24602         
24603         this.combo = Roo.factory(this.combo, Roo.form);
24604         this.combo.onRender(ct, position);
24605         if (typeof(this.combo.width) != 'undefined') {
24606             this.combo.onResize(this.combo.width,0);
24607         }
24608         
24609         this.combo.initEvents();
24610         
24611         // assigned so form know we need to do this..
24612         this.store          = this.combo.store;
24613         this.valueField     = this.combo.valueField;
24614         this.displayField   = this.combo.displayField ;
24615         
24616         
24617         this.combo.wrap.addClass('x-cbarray-grp');
24618         
24619         var cbwrap = this.combo.wrap.createChild(
24620             {tag: 'div', cls: 'x-cbarray-cb'},
24621             this.combo.el.dom
24622         );
24623         
24624              
24625         this.hiddenEl = this.combo.wrap.createChild({
24626             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
24627         });
24628         this.el = this.combo.wrap.createChild({
24629             tag: 'input',  type:'hidden' , name: this.name, value : ''
24630         });
24631          //   this.el.dom.removeAttribute("name");
24632         
24633         
24634         this.outerWrap = this.combo.wrap;
24635         this.wrap = cbwrap;
24636         
24637         this.outerWrap.setWidth(this.width);
24638         this.outerWrap.dom.removeChild(this.el.dom);
24639         
24640         this.wrap.dom.appendChild(this.el.dom);
24641         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
24642         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
24643         
24644         this.combo.trigger.setStyle('position','relative');
24645         this.combo.trigger.setStyle('left', '0px');
24646         this.combo.trigger.setStyle('top', '2px');
24647         
24648         this.combo.el.setStyle('vertical-align', 'text-bottom');
24649         
24650         //this.trigger.setStyle('vertical-align', 'top');
24651         
24652         // this should use the code from combo really... on('add' ....)
24653         if (this.adder) {
24654             
24655         
24656             this.adder = this.outerWrap.createChild(
24657                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
24658             var _t = this;
24659             this.adder.on('click', function(e) {
24660                 _t.fireEvent('adderclick', this, e);
24661             }, _t);
24662         }
24663         //var _t = this;
24664         //this.adder.on('click', this.onAddClick, _t);
24665         
24666         
24667         this.combo.on('select', function(cb, rec, ix) {
24668             this.addItem(rec.data);
24669             
24670             cb.setValue('');
24671             cb.el.dom.value = '';
24672             //cb.lastData = rec.data;
24673             // add to list
24674             
24675         }, this);
24676         
24677         
24678     },
24679     
24680     
24681     getName: function()
24682     {
24683         // returns hidden if it's set..
24684         if (!this.rendered) {return ''};
24685         return  this.hiddenName ? this.hiddenName : this.name;
24686         
24687     },
24688     
24689     
24690     onResize: function(w, h){
24691         
24692         return;
24693         // not sure if this is needed..
24694         //this.combo.onResize(w,h);
24695         
24696         if(typeof w != 'number'){
24697             // we do not handle it!?!?
24698             return;
24699         }
24700         var tw = this.combo.trigger.getWidth();
24701         tw += this.addicon ? this.addicon.getWidth() : 0;
24702         tw += this.editicon ? this.editicon.getWidth() : 0;
24703         var x = w - tw;
24704         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
24705             
24706         this.combo.trigger.setStyle('left', '0px');
24707         
24708         if(this.list && this.listWidth === undefined){
24709             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
24710             this.list.setWidth(lw);
24711             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
24712         }
24713         
24714     
24715         
24716     },
24717     
24718     addItem: function(rec)
24719     {
24720         var valueField = this.combo.valueField;
24721         var displayField = this.combo.displayField;
24722         if (this.items.indexOfKey(rec[valueField]) > -1) {
24723             //console.log("GOT " + rec.data.id);
24724             return;
24725         }
24726         
24727         var x = new Roo.form.ComboBoxArray.Item({
24728             //id : rec[this.idField],
24729             data : rec,
24730             displayField : displayField ,
24731             tipField : displayField ,
24732             cb : this
24733         });
24734         // use the 
24735         this.items.add(rec[valueField],x);
24736         // add it before the element..
24737         this.updateHiddenEl();
24738         x.render(this.outerWrap, this.wrap.dom);
24739         // add the image handler..
24740     },
24741     
24742     updateHiddenEl : function()
24743     {
24744         this.validate();
24745         if (!this.hiddenEl) {
24746             return;
24747         }
24748         var ar = [];
24749         var idField = this.combo.valueField;
24750         
24751         this.items.each(function(f) {
24752             ar.push(f.data[idField]);
24753            
24754         });
24755         this.hiddenEl.dom.value = ar.join(',');
24756         this.validate();
24757     },
24758     
24759     reset : function()
24760     {
24761         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
24762         this.items.each(function(f) {
24763            f.remove(); 
24764         });
24765         this.el.dom.value = '';
24766         if (this.hiddenEl) {
24767             this.hiddenEl.dom.value = '';
24768         }
24769         
24770     },
24771     getValue: function()
24772     {
24773         return this.hiddenEl ? this.hiddenEl.dom.value : '';
24774     },
24775     setValue: function(v) // not a valid action - must use addItems..
24776     {
24777          
24778         this.reset();
24779         
24780         
24781         
24782         if (this.store.isLocal && (typeof(v) == 'string')) {
24783             // then we can use the store to find the values..
24784             // comma seperated at present.. this needs to allow JSON based encoding..
24785             this.hiddenEl.value  = v;
24786             var v_ar = [];
24787             Roo.each(v.split(','), function(k) {
24788                 Roo.log("CHECK " + this.valueField + ',' + k);
24789                 var li = this.store.query(this.valueField, k);
24790                 if (!li.length) {
24791                     return;
24792                 }
24793                 var add = {};
24794                 add[this.valueField] = k;
24795                 add[this.displayField] = li.item(0).data[this.displayField];
24796                 
24797                 this.addItem(add);
24798             }, this) 
24799              
24800         }
24801         if (typeof(v) == 'object') {
24802             // then let's assume it's an array of objects..
24803             Roo.each(v, function(l) {
24804                 this.addItem(l);
24805             }, this);
24806              
24807         }
24808         
24809         
24810     },
24811     setFromData: function(v)
24812     {
24813         // this recieves an object, if setValues is called.
24814         this.reset();
24815         this.el.dom.value = v[this.displayField];
24816         this.hiddenEl.dom.value = v[this.valueField];
24817         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
24818             return;
24819         }
24820         var kv = v[this.valueField];
24821         var dv = v[this.displayField];
24822         kv = typeof(kv) != 'string' ? '' : kv;
24823         dv = typeof(dv) != 'string' ? '' : dv;
24824         
24825         
24826         var keys = kv.split(',');
24827         var display = dv.split(',');
24828         for (var i = 0 ; i < keys.length; i++) {
24829             
24830             add = {};
24831             add[this.valueField] = keys[i];
24832             add[this.displayField] = display[i];
24833             this.addItem(add);
24834         }
24835       
24836         
24837     },
24838     
24839     
24840     validateValue : function(value){
24841         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
24842         
24843     }
24844     
24845 });
24846
24847
24848
24849 /**
24850  * @class Roo.form.ComboBoxArray.Item
24851  * @extends Roo.BoxComponent
24852  * A selected item in the list
24853  *  Fred [x]  Brian [x]  [Pick another |v]
24854  * 
24855  * @constructor
24856  * Create a new item.
24857  * @param {Object} config Configuration options
24858  */
24859  
24860 Roo.form.ComboBoxArray.Item = function(config) {
24861     config.id = Roo.id();
24862     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
24863 }
24864
24865 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
24866     data : {},
24867     cb: false,
24868     displayField : false,
24869     tipField : false,
24870     
24871     
24872     defaultAutoCreate : {
24873         tag: 'div',
24874         cls: 'x-cbarray-item',
24875         cn : [ 
24876             { tag: 'div' },
24877             {
24878                 tag: 'img',
24879                 width:16,
24880                 height : 16,
24881                 src : Roo.BLANK_IMAGE_URL ,
24882                 align: 'center'
24883             }
24884         ]
24885         
24886     },
24887     
24888  
24889     onRender : function(ct, position)
24890     {
24891         Roo.form.Field.superclass.onRender.call(this, ct, position);
24892         
24893         if(!this.el){
24894             var cfg = this.getAutoCreate();
24895             this.el = ct.createChild(cfg, position);
24896         }
24897         
24898         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
24899         
24900         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
24901             this.cb.renderer(this.data) :
24902             String.format('{0}',this.data[this.displayField]);
24903         
24904             
24905         this.el.child('div').dom.setAttribute('qtip',
24906                         String.format('{0}',this.data[this.tipField])
24907         );
24908         
24909         this.el.child('img').on('click', this.remove, this);
24910         
24911     },
24912    
24913     remove : function()
24914     {
24915         
24916         this.cb.items.remove(this);
24917         this.el.child('img').un('click', this.remove, this);
24918         this.el.remove();
24919         this.cb.updateHiddenEl();
24920     }
24921     
24922     
24923 });/*
24924  * Based on:
24925  * Ext JS Library 1.1.1
24926  * Copyright(c) 2006-2007, Ext JS, LLC.
24927  *
24928  * Originally Released Under LGPL - original licence link has changed is not relivant.
24929  *
24930  * Fork - LGPL
24931  * <script type="text/javascript">
24932  */
24933 /**
24934  * @class Roo.form.Checkbox
24935  * @extends Roo.form.Field
24936  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
24937  * @constructor
24938  * Creates a new Checkbox
24939  * @param {Object} config Configuration options
24940  */
24941 Roo.form.Checkbox = function(config){
24942     Roo.form.Checkbox.superclass.constructor.call(this, config);
24943     this.addEvents({
24944         /**
24945          * @event check
24946          * Fires when the checkbox is checked or unchecked.
24947              * @param {Roo.form.Checkbox} this This checkbox
24948              * @param {Boolean} checked The new checked value
24949              */
24950         check : true
24951     });
24952 };
24953
24954 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
24955     /**
24956      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
24957      */
24958     focusClass : undefined,
24959     /**
24960      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
24961      */
24962     fieldClass: "x-form-field",
24963     /**
24964      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
24965      */
24966     checked: false,
24967     /**
24968      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
24969      * {tag: "input", type: "checkbox", autocomplete: "off"})
24970      */
24971     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
24972     /**
24973      * @cfg {String} boxLabel The text that appears beside the checkbox
24974      */
24975     boxLabel : "",
24976     /**
24977      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
24978      */  
24979     inputValue : '1',
24980     /**
24981      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24982      */
24983      valueOff: '0', // value when not checked..
24984
24985     actionMode : 'viewEl', 
24986     //
24987     // private
24988     itemCls : 'x-menu-check-item x-form-item',
24989     groupClass : 'x-menu-group-item',
24990     inputType : 'hidden',
24991     
24992     
24993     inSetChecked: false, // check that we are not calling self...
24994     
24995     inputElement: false, // real input element?
24996     basedOn: false, // ????
24997     
24998     isFormField: true, // not sure where this is needed!!!!
24999
25000     onResize : function(){
25001         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
25002         if(!this.boxLabel){
25003             this.el.alignTo(this.wrap, 'c-c');
25004         }
25005     },
25006
25007     initEvents : function(){
25008         Roo.form.Checkbox.superclass.initEvents.call(this);
25009         this.el.on("click", this.onClick,  this);
25010         this.el.on("change", this.onClick,  this);
25011     },
25012
25013
25014     getResizeEl : function(){
25015         return this.wrap;
25016     },
25017
25018     getPositionEl : function(){
25019         return this.wrap;
25020     },
25021
25022     // private
25023     onRender : function(ct, position){
25024         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
25025         /*
25026         if(this.inputValue !== undefined){
25027             this.el.dom.value = this.inputValue;
25028         }
25029         */
25030         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
25031         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
25032         var viewEl = this.wrap.createChild({ 
25033             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
25034         this.viewEl = viewEl;   
25035         this.wrap.on('click', this.onClick,  this); 
25036         
25037         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
25038         this.el.on('propertychange', this.setFromHidden,  this);  //ie
25039         
25040         
25041         
25042         if(this.boxLabel){
25043             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
25044         //    viewEl.on('click', this.onClick,  this); 
25045         }
25046         //if(this.checked){
25047             this.setChecked(this.checked);
25048         //}else{
25049             //this.checked = this.el.dom;
25050         //}
25051
25052     },
25053
25054     // private
25055     initValue : Roo.emptyFn,
25056
25057     /**
25058      * Returns the checked state of the checkbox.
25059      * @return {Boolean} True if checked, else false
25060      */
25061     getValue : function(){
25062         if(this.el){
25063             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
25064         }
25065         return this.valueOff;
25066         
25067     },
25068
25069         // private
25070     onClick : function(){ 
25071         this.setChecked(!this.checked);
25072
25073         //if(this.el.dom.checked != this.checked){
25074         //    this.setValue(this.el.dom.checked);
25075        // }
25076     },
25077
25078     /**
25079      * Sets the checked state of the checkbox.
25080      * On is always based on a string comparison between inputValue and the param.
25081      * @param {Boolean/String} value - the value to set 
25082      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
25083      */
25084     setValue : function(v,suppressEvent){
25085         
25086         
25087         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
25088         //if(this.el && this.el.dom){
25089         //    this.el.dom.checked = this.checked;
25090         //    this.el.dom.defaultChecked = this.checked;
25091         //}
25092         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
25093         //this.fireEvent("check", this, this.checked);
25094     },
25095     // private..
25096     setChecked : function(state,suppressEvent)
25097     {
25098         if (this.inSetChecked) {
25099             this.checked = state;
25100             return;
25101         }
25102         
25103     
25104         if(this.wrap){
25105             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
25106         }
25107         this.checked = state;
25108         if(suppressEvent !== true){
25109             this.fireEvent('check', this, state);
25110         }
25111         this.inSetChecked = true;
25112         this.el.dom.value = state ? this.inputValue : this.valueOff;
25113         this.inSetChecked = false;
25114         
25115     },
25116     // handle setting of hidden value by some other method!!?!?
25117     setFromHidden: function()
25118     {
25119         if(!this.el){
25120             return;
25121         }
25122         //console.log("SET FROM HIDDEN");
25123         //alert('setFrom hidden');
25124         this.setValue(this.el.dom.value);
25125     },
25126     
25127     onDestroy : function()
25128     {
25129         if(this.viewEl){
25130             Roo.get(this.viewEl).remove();
25131         }
25132          
25133         Roo.form.Checkbox.superclass.onDestroy.call(this);
25134     }
25135
25136 });/*
25137  * Based on:
25138  * Ext JS Library 1.1.1
25139  * Copyright(c) 2006-2007, Ext JS, LLC.
25140  *
25141  * Originally Released Under LGPL - original licence link has changed is not relivant.
25142  *
25143  * Fork - LGPL
25144  * <script type="text/javascript">
25145  */
25146  
25147 /**
25148  * @class Roo.form.Radio
25149  * @extends Roo.form.Checkbox
25150  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
25151  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
25152  * @constructor
25153  * Creates a new Radio
25154  * @param {Object} config Configuration options
25155  */
25156 Roo.form.Radio = function(){
25157     Roo.form.Radio.superclass.constructor.apply(this, arguments);
25158 };
25159 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
25160     inputType: 'radio',
25161
25162     /**
25163      * If this radio is part of a group, it will return the selected value
25164      * @return {String}
25165      */
25166     getGroupValue : function(){
25167         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
25168     },
25169     
25170     
25171     onRender : function(ct, position){
25172         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
25173         
25174         if(this.inputValue !== undefined){
25175             this.el.dom.value = this.inputValue;
25176         }
25177          
25178         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
25179         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
25180         //var viewEl = this.wrap.createChild({ 
25181         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
25182         //this.viewEl = viewEl;   
25183         //this.wrap.on('click', this.onClick,  this); 
25184         
25185         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
25186         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
25187         
25188         
25189         
25190         if(this.boxLabel){
25191             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
25192         //    viewEl.on('click', this.onClick,  this); 
25193         }
25194          if(this.checked){
25195             this.el.dom.checked =   'checked' ;
25196         }
25197          
25198     } 
25199     
25200     
25201 });//<script type="text/javascript">
25202
25203 /*
25204  * Ext JS Library 1.1.1
25205  * Copyright(c) 2006-2007, Ext JS, LLC.
25206  * licensing@extjs.com
25207  * 
25208  * http://www.extjs.com/license
25209  */
25210  
25211  /*
25212   * 
25213   * Known bugs:
25214   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
25215   * - IE ? - no idea how much works there.
25216   * 
25217   * 
25218   * 
25219   */
25220  
25221
25222 /**
25223  * @class Ext.form.HtmlEditor
25224  * @extends Ext.form.Field
25225  * Provides a lightweight HTML Editor component.
25226  *
25227  * This has been tested on Fireforx / Chrome.. IE may not be so great..
25228  * 
25229  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
25230  * supported by this editor.</b><br/><br/>
25231  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
25232  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25233  */
25234 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
25235       /**
25236      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25237      */
25238     toolbars : false,
25239     /**
25240      * @cfg {String} createLinkText The default text for the create link prompt
25241      */
25242     createLinkText : 'Please enter the URL for the link:',
25243     /**
25244      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
25245      */
25246     defaultLinkValue : 'http:/'+'/',
25247    
25248      /**
25249      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25250      *                        Roo.resizable.
25251      */
25252     resizable : false,
25253      /**
25254      * @cfg {Number} height (in pixels)
25255      */   
25256     height: 300,
25257    /**
25258      * @cfg {Number} width (in pixels)
25259      */   
25260     width: 500,
25261     
25262     /**
25263      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25264      * 
25265      */
25266     stylesheets: false,
25267     
25268     // id of frame..
25269     frameId: false,
25270     
25271     // private properties
25272     validationEvent : false,
25273     deferHeight: true,
25274     initialized : false,
25275     activated : false,
25276     sourceEditMode : false,
25277     onFocus : Roo.emptyFn,
25278     iframePad:3,
25279     hideMode:'offsets',
25280     
25281     defaultAutoCreate : { // modified by initCompnoent..
25282         tag: "textarea",
25283         style:"width:500px;height:300px;",
25284         autocomplete: "off"
25285     },
25286
25287     // private
25288     initComponent : function(){
25289         this.addEvents({
25290             /**
25291              * @event initialize
25292              * Fires when the editor is fully initialized (including the iframe)
25293              * @param {HtmlEditor} this
25294              */
25295             initialize: true,
25296             /**
25297              * @event activate
25298              * Fires when the editor is first receives the focus. Any insertion must wait
25299              * until after this event.
25300              * @param {HtmlEditor} this
25301              */
25302             activate: true,
25303              /**
25304              * @event beforesync
25305              * Fires before the textarea is updated with content from the editor iframe. Return false
25306              * to cancel the sync.
25307              * @param {HtmlEditor} this
25308              * @param {String} html
25309              */
25310             beforesync: true,
25311              /**
25312              * @event beforepush
25313              * Fires before the iframe editor is updated with content from the textarea. Return false
25314              * to cancel the push.
25315              * @param {HtmlEditor} this
25316              * @param {String} html
25317              */
25318             beforepush: true,
25319              /**
25320              * @event sync
25321              * Fires when the textarea is updated with content from the editor iframe.
25322              * @param {HtmlEditor} this
25323              * @param {String} html
25324              */
25325             sync: true,
25326              /**
25327              * @event push
25328              * Fires when the iframe editor is updated with content from the textarea.
25329              * @param {HtmlEditor} this
25330              * @param {String} html
25331              */
25332             push: true,
25333              /**
25334              * @event editmodechange
25335              * Fires when the editor switches edit modes
25336              * @param {HtmlEditor} this
25337              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25338              */
25339             editmodechange: true,
25340             /**
25341              * @event editorevent
25342              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25343              * @param {HtmlEditor} this
25344              */
25345             editorevent: true
25346         });
25347         this.defaultAutoCreate =  {
25348             tag: "textarea",
25349             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
25350             autocomplete: "off"
25351         };
25352     },
25353
25354     /**
25355      * Protected method that will not generally be called directly. It
25356      * is called when the editor creates its toolbar. Override this method if you need to
25357      * add custom toolbar buttons.
25358      * @param {HtmlEditor} editor
25359      */
25360     createToolbar : function(editor){
25361         if (!editor.toolbars || !editor.toolbars.length) {
25362             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
25363         }
25364         
25365         for (var i =0 ; i < editor.toolbars.length;i++) {
25366             editor.toolbars[i] = Roo.factory(
25367                     typeof(editor.toolbars[i]) == 'string' ?
25368                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
25369                 Roo.form.HtmlEditor);
25370             editor.toolbars[i].init(editor);
25371         }
25372          
25373         
25374     },
25375
25376     /**
25377      * Protected method that will not generally be called directly. It
25378      * is called when the editor initializes the iframe with HTML contents. Override this method if you
25379      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25380      */
25381     getDocMarkup : function(){
25382         // body styles..
25383         var st = '';
25384         if (this.stylesheets === false) {
25385             
25386             Roo.get(document.head).select('style').each(function(node) {
25387                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25388             });
25389             
25390             Roo.get(document.head).select('link').each(function(node) { 
25391                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25392             });
25393             
25394         } else if (!this.stylesheets.length) {
25395                 // simple..
25396                 st = '<style type="text/css">' +
25397                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25398                    '</style>';
25399         } else {
25400             Roo.each(this.stylesheets, function(s) {
25401                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
25402             });
25403             
25404         }
25405         
25406         st +=  '<style type="text/css">' +
25407             'IMG { cursor: pointer } ' +
25408         '</style>';
25409
25410         
25411         return '<html><head>' + st  +
25412             //<style type="text/css">' +
25413             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25414             //'</style>' +
25415             ' </head><body class="roo-htmleditor-body"></body></html>';
25416     },
25417
25418     // private
25419     onRender : function(ct, position)
25420     {
25421         var _t = this;
25422         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
25423         this.el.dom.style.border = '0 none';
25424         this.el.dom.setAttribute('tabIndex', -1);
25425         this.el.addClass('x-hidden');
25426         if(Roo.isIE){ // fix IE 1px bogus margin
25427             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25428         }
25429         this.wrap = this.el.wrap({
25430             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25431         });
25432         
25433         if (this.resizable) {
25434             this.resizeEl = new Roo.Resizable(this.wrap, {
25435                 pinned : true,
25436                 wrap: true,
25437                 dynamic : true,
25438                 minHeight : this.height,
25439                 height: this.height,
25440                 handles : this.resizable,
25441                 width: this.width,
25442                 listeners : {
25443                     resize : function(r, w, h) {
25444                         _t.onResize(w,h); // -something
25445                     }
25446                 }
25447             });
25448             
25449         }
25450
25451         this.frameId = Roo.id();
25452         
25453         this.createToolbar(this);
25454         
25455       
25456         
25457         var iframe = this.wrap.createChild({
25458             tag: 'iframe',
25459             id: this.frameId,
25460             name: this.frameId,
25461             frameBorder : 'no',
25462             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
25463         }, this.el
25464         );
25465         
25466        // console.log(iframe);
25467         //this.wrap.dom.appendChild(iframe);
25468
25469         this.iframe = iframe.dom;
25470
25471          this.assignDocWin();
25472         
25473         this.doc.designMode = 'on';
25474        
25475         this.doc.open();
25476         this.doc.write(this.getDocMarkup());
25477         this.doc.close();
25478
25479         
25480         var task = { // must defer to wait for browser to be ready
25481             run : function(){
25482                 //console.log("run task?" + this.doc.readyState);
25483                 this.assignDocWin();
25484                 if(this.doc.body || this.doc.readyState == 'complete'){
25485                     try {
25486                         this.doc.designMode="on";
25487                     } catch (e) {
25488                         return;
25489                     }
25490                     Roo.TaskMgr.stop(task);
25491                     this.initEditor.defer(10, this);
25492                 }
25493             },
25494             interval : 10,
25495             duration:10000,
25496             scope: this
25497         };
25498         Roo.TaskMgr.start(task);
25499
25500         if(!this.width){
25501             this.setSize(this.wrap.getSize());
25502         }
25503         if (this.resizeEl) {
25504             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25505             // should trigger onReize..
25506         }
25507     },
25508
25509     // private
25510     onResize : function(w, h)
25511     {
25512         //Roo.log('resize: ' +w + ',' + h );
25513         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
25514         if(this.el && this.iframe){
25515             if(typeof w == 'number'){
25516                 var aw = w - this.wrap.getFrameWidth('lr');
25517                 this.el.setWidth(this.adjustWidth('textarea', aw));
25518                 this.iframe.style.width = aw + 'px';
25519             }
25520             if(typeof h == 'number'){
25521                 var tbh = 0;
25522                 for (var i =0; i < this.toolbars.length;i++) {
25523                     // fixme - ask toolbars for heights?
25524                     tbh += this.toolbars[i].tb.el.getHeight();
25525                     if (this.toolbars[i].footer) {
25526                         tbh += this.toolbars[i].footer.el.getHeight();
25527                     }
25528                 }
25529                 
25530                 
25531                 
25532                 
25533                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25534                 ah -= 5; // knock a few pixes off for look..
25535                 this.el.setHeight(this.adjustWidth('textarea', ah));
25536                 this.iframe.style.height = ah + 'px';
25537                 if(this.doc){
25538                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
25539                 }
25540             }
25541         }
25542     },
25543
25544     /**
25545      * Toggles the editor between standard and source edit mode.
25546      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25547      */
25548     toggleSourceEdit : function(sourceEditMode){
25549         
25550         this.sourceEditMode = sourceEditMode === true;
25551         
25552         if(this.sourceEditMode){
25553 //            Roo.log('in');
25554 //            Roo.log(this.syncValue());
25555             this.syncValue();
25556             this.iframe.className = 'x-hidden';
25557             this.el.removeClass('x-hidden');
25558             this.el.dom.removeAttribute('tabIndex');
25559             this.el.focus();
25560         }else{
25561 //            Roo.log('out')
25562 //            Roo.log(this.pushValue()); 
25563             this.pushValue();
25564             this.iframe.className = '';
25565             this.el.addClass('x-hidden');
25566             this.el.dom.setAttribute('tabIndex', -1);
25567             this.deferFocus();
25568         }
25569         this.setSize(this.wrap.getSize());
25570         this.fireEvent('editmodechange', this, this.sourceEditMode);
25571     },
25572
25573     // private used internally
25574     createLink : function(){
25575         var url = prompt(this.createLinkText, this.defaultLinkValue);
25576         if(url && url != 'http:/'+'/'){
25577             this.relayCmd('createlink', url);
25578         }
25579     },
25580
25581     // private (for BoxComponent)
25582     adjustSize : Roo.BoxComponent.prototype.adjustSize,
25583
25584     // private (for BoxComponent)
25585     getResizeEl : function(){
25586         return this.wrap;
25587     },
25588
25589     // private (for BoxComponent)
25590     getPositionEl : function(){
25591         return this.wrap;
25592     },
25593
25594     // private
25595     initEvents : function(){
25596         this.originalValue = this.getValue();
25597     },
25598
25599     /**
25600      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25601      * @method
25602      */
25603     markInvalid : Roo.emptyFn,
25604     /**
25605      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25606      * @method
25607      */
25608     clearInvalid : Roo.emptyFn,
25609
25610     setValue : function(v){
25611         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
25612         this.pushValue();
25613     },
25614
25615     /**
25616      * Protected method that will not generally be called directly. If you need/want
25617      * custom HTML cleanup, this is the method you should override.
25618      * @param {String} html The HTML to be cleaned
25619      * return {String} The cleaned HTML
25620      */
25621     cleanHtml : function(html){
25622         html = String(html);
25623         if(html.length > 5){
25624             if(Roo.isSafari){ // strip safari nonsense
25625                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25626             }
25627         }
25628         if(html == '&nbsp;'){
25629             html = '';
25630         }
25631         return html;
25632     },
25633
25634     /**
25635      * Protected method that will not generally be called directly. Syncs the contents
25636      * of the editor iframe with the textarea.
25637      */
25638     syncValue : function(){
25639         if(this.initialized){
25640             var bd = (this.doc.body || this.doc.documentElement);
25641             //this.cleanUpPaste(); -- this is done else where and causes havoc..
25642             var html = bd.innerHTML;
25643             if(Roo.isSafari){
25644                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25645                 var m = bs.match(/text-align:(.*?);/i);
25646                 if(m && m[1]){
25647                     html = '<div style="'+m[0]+'">' + html + '</div>';
25648                 }
25649             }
25650             html = this.cleanHtml(html);
25651             // fix up the special chars.. normaly like back quotes in word...
25652             // however we do not want to do this with chinese..
25653             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
25654                 var cc = b.charCodeAt();
25655                 if (
25656                     (cc >= 0x4E00 && cc < 0xA000 ) ||
25657                     (cc >= 0x3400 && cc < 0x4E00 ) ||
25658                     (cc >= 0xf900 && cc < 0xfb00 )
25659                 ) {
25660                         return b;
25661                 }
25662                 return "&#"+cc+";" 
25663             });
25664             if(this.fireEvent('beforesync', this, html) !== false){
25665                 this.el.dom.value = html;
25666                 this.fireEvent('sync', this, html);
25667             }
25668         }
25669     },
25670
25671     /**
25672      * Protected method that will not generally be called directly. Pushes the value of the textarea
25673      * into the iframe editor.
25674      */
25675     pushValue : function(){
25676         if(this.initialized){
25677             var v = this.el.dom.value;
25678             
25679             if(v.length < 1){
25680                 v = '&#160;';
25681             }
25682             
25683             if(this.fireEvent('beforepush', this, v) !== false){
25684                 var d = (this.doc.body || this.doc.documentElement);
25685                 d.innerHTML = v;
25686                 this.cleanUpPaste();
25687                 this.el.dom.value = d.innerHTML;
25688                 this.fireEvent('push', this, v);
25689             }
25690         }
25691     },
25692
25693     // private
25694     deferFocus : function(){
25695         this.focus.defer(10, this);
25696     },
25697
25698     // doc'ed in Field
25699     focus : function(){
25700         if(this.win && !this.sourceEditMode){
25701             this.win.focus();
25702         }else{
25703             this.el.focus();
25704         }
25705     },
25706     
25707     assignDocWin: function()
25708     {
25709         var iframe = this.iframe;
25710         
25711          if(Roo.isIE){
25712             this.doc = iframe.contentWindow.document;
25713             this.win = iframe.contentWindow;
25714         } else {
25715             if (!Roo.get(this.frameId)) {
25716                 return;
25717             }
25718             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25719             this.win = Roo.get(this.frameId).dom.contentWindow;
25720         }
25721     },
25722     
25723     // private
25724     initEditor : function(){
25725         //console.log("INIT EDITOR");
25726         this.assignDocWin();
25727         
25728         
25729         
25730         this.doc.designMode="on";
25731         this.doc.open();
25732         this.doc.write(this.getDocMarkup());
25733         this.doc.close();
25734         
25735         var dbody = (this.doc.body || this.doc.documentElement);
25736         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25737         // this copies styles from the containing element into thsi one..
25738         // not sure why we need all of this..
25739         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25740         ss['background-attachment'] = 'fixed'; // w3c
25741         dbody.bgProperties = 'fixed'; // ie
25742         Roo.DomHelper.applyStyles(dbody, ss);
25743         Roo.EventManager.on(this.doc, {
25744             //'mousedown': this.onEditorEvent,
25745             'mouseup': this.onEditorEvent,
25746             'dblclick': this.onEditorEvent,
25747             'click': this.onEditorEvent,
25748             'keyup': this.onEditorEvent,
25749             buffer:100,
25750             scope: this
25751         });
25752         if(Roo.isGecko){
25753             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25754         }
25755         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25756             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25757         }
25758         this.initialized = true;
25759
25760         this.fireEvent('initialize', this);
25761         this.pushValue();
25762     },
25763
25764     // private
25765     onDestroy : function(){
25766         
25767         
25768         
25769         if(this.rendered){
25770             
25771             for (var i =0; i < this.toolbars.length;i++) {
25772                 // fixme - ask toolbars for heights?
25773                 this.toolbars[i].onDestroy();
25774             }
25775             
25776             this.wrap.dom.innerHTML = '';
25777             this.wrap.remove();
25778         }
25779     },
25780
25781     // private
25782     onFirstFocus : function(){
25783         
25784         this.assignDocWin();
25785         
25786         
25787         this.activated = true;
25788         for (var i =0; i < this.toolbars.length;i++) {
25789             this.toolbars[i].onFirstFocus();
25790         }
25791        
25792         if(Roo.isGecko){ // prevent silly gecko errors
25793             this.win.focus();
25794             var s = this.win.getSelection();
25795             if(!s.focusNode || s.focusNode.nodeType != 3){
25796                 var r = s.getRangeAt(0);
25797                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25798                 r.collapse(true);
25799                 this.deferFocus();
25800             }
25801             try{
25802                 this.execCmd('useCSS', true);
25803                 this.execCmd('styleWithCSS', false);
25804             }catch(e){}
25805         }
25806         this.fireEvent('activate', this);
25807     },
25808
25809     // private
25810     adjustFont: function(btn){
25811         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
25812         //if(Roo.isSafari){ // safari
25813         //    adjust *= 2;
25814        // }
25815         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
25816         if(Roo.isSafari){ // safari
25817             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
25818             v =  (v < 10) ? 10 : v;
25819             v =  (v > 48) ? 48 : v;
25820             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
25821             
25822         }
25823         
25824         
25825         v = Math.max(1, v+adjust);
25826         
25827         this.execCmd('FontSize', v  );
25828     },
25829
25830     onEditorEvent : function(e){
25831         this.fireEvent('editorevent', this, e);
25832       //  this.updateToolbar();
25833         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
25834     },
25835
25836     insertTag : function(tg)
25837     {
25838         // could be a bit smarter... -> wrap the current selected tRoo..
25839         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
25840             
25841             range = this.createRange(this.getSelection());
25842             var wrappingNode = this.doc.createElement(tg.toLowerCase());
25843             wrappingNode.appendChild(range.extractContents());
25844             range.insertNode(wrappingNode);
25845
25846             return;
25847             
25848             
25849             
25850         }
25851         this.execCmd("formatblock",   tg);
25852         
25853     },
25854     
25855     insertText : function(txt)
25856     {
25857         
25858         
25859         var range = this.createRange();
25860         range.deleteContents();
25861                //alert(Sender.getAttribute('label'));
25862                
25863         range.insertNode(this.doc.createTextNode(txt));
25864     } ,
25865     
25866     // private
25867     relayBtnCmd : function(btn){
25868         this.relayCmd(btn.cmd);
25869     },
25870
25871     /**
25872      * Executes a Midas editor command on the editor document and performs necessary focus and
25873      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25874      * @param {String} cmd The Midas command
25875      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25876      */
25877     relayCmd : function(cmd, value){
25878         this.win.focus();
25879         this.execCmd(cmd, value);
25880         this.fireEvent('editorevent', this);
25881         //this.updateToolbar();
25882         this.deferFocus();
25883     },
25884
25885     /**
25886      * Executes a Midas editor command directly on the editor document.
25887      * For visual commands, you should use {@link #relayCmd} instead.
25888      * <b>This should only be called after the editor is initialized.</b>
25889      * @param {String} cmd The Midas command
25890      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25891      */
25892     execCmd : function(cmd, value){
25893         this.doc.execCommand(cmd, false, value === undefined ? null : value);
25894         this.syncValue();
25895     },
25896  
25897  
25898    
25899     /**
25900      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25901      * to insert tRoo.
25902      * @param {String} text | dom node.. 
25903      */
25904     insertAtCursor : function(text)
25905     {
25906         
25907         
25908         
25909         if(!this.activated){
25910             return;
25911         }
25912         /*
25913         if(Roo.isIE){
25914             this.win.focus();
25915             var r = this.doc.selection.createRange();
25916             if(r){
25917                 r.collapse(true);
25918                 r.pasteHTML(text);
25919                 this.syncValue();
25920                 this.deferFocus();
25921             
25922             }
25923             return;
25924         }
25925         */
25926         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25927             this.win.focus();
25928             
25929             
25930             // from jquery ui (MIT licenced)
25931             var range, node;
25932             var win = this.win;
25933             
25934             if (win.getSelection && win.getSelection().getRangeAt) {
25935                 range = win.getSelection().getRangeAt(0);
25936                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25937                 range.insertNode(node);
25938             } else if (win.document.selection && win.document.selection.createRange) {
25939                 // no firefox support
25940                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25941                 win.document.selection.createRange().pasteHTML(txt);
25942             } else {
25943                 // no firefox support
25944                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25945                 this.execCmd('InsertHTML', txt);
25946             } 
25947             
25948             this.syncValue();
25949             
25950             this.deferFocus();
25951         }
25952     },
25953  // private
25954     mozKeyPress : function(e){
25955         if(e.ctrlKey){
25956             var c = e.getCharCode(), cmd;
25957           
25958             if(c > 0){
25959                 c = String.fromCharCode(c).toLowerCase();
25960                 switch(c){
25961                     case 'b':
25962                         cmd = 'bold';
25963                         break;
25964                     case 'i':
25965                         cmd = 'italic';
25966                         break;
25967                     
25968                     case 'u':
25969                         cmd = 'underline';
25970                         break;
25971                     
25972                     case 'v':
25973                         this.cleanUpPaste.defer(100, this);
25974                         return;
25975                         
25976                 }
25977                 if(cmd){
25978                     this.win.focus();
25979                     this.execCmd(cmd);
25980                     this.deferFocus();
25981                     e.preventDefault();
25982                 }
25983                 
25984             }
25985         }
25986     },
25987
25988     // private
25989     fixKeys : function(){ // load time branching for fastest keydown performance
25990         if(Roo.isIE){
25991             return function(e){
25992                 var k = e.getKey(), r;
25993                 if(k == e.TAB){
25994                     e.stopEvent();
25995                     r = this.doc.selection.createRange();
25996                     if(r){
25997                         r.collapse(true);
25998                         r.pasteHTML('&#160;&#160;&#160;&#160;');
25999                         this.deferFocus();
26000                     }
26001                     return;
26002                 }
26003                 
26004                 if(k == e.ENTER){
26005                     r = this.doc.selection.createRange();
26006                     if(r){
26007                         var target = r.parentElement();
26008                         if(!target || target.tagName.toLowerCase() != 'li'){
26009                             e.stopEvent();
26010                             r.pasteHTML('<br />');
26011                             r.collapse(false);
26012                             r.select();
26013                         }
26014                     }
26015                 }
26016                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26017                     this.cleanUpPaste.defer(100, this);
26018                     return;
26019                 }
26020                 
26021                 
26022             };
26023         }else if(Roo.isOpera){
26024             return function(e){
26025                 var k = e.getKey();
26026                 if(k == e.TAB){
26027                     e.stopEvent();
26028                     this.win.focus();
26029                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
26030                     this.deferFocus();
26031                 }
26032                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26033                     this.cleanUpPaste.defer(100, this);
26034                     return;
26035                 }
26036                 
26037             };
26038         }else if(Roo.isSafari){
26039             return function(e){
26040                 var k = e.getKey();
26041                 
26042                 if(k == e.TAB){
26043                     e.stopEvent();
26044                     this.execCmd('InsertText','\t');
26045                     this.deferFocus();
26046                     return;
26047                 }
26048                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26049                     this.cleanUpPaste.defer(100, this);
26050                     return;
26051                 }
26052                 
26053              };
26054         }
26055     }(),
26056     
26057     getAllAncestors: function()
26058     {
26059         var p = this.getSelectedNode();
26060         var a = [];
26061         if (!p) {
26062             a.push(p); // push blank onto stack..
26063             p = this.getParentElement();
26064         }
26065         
26066         
26067         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26068             a.push(p);
26069             p = p.parentNode;
26070         }
26071         a.push(this.doc.body);
26072         return a;
26073     },
26074     lastSel : false,
26075     lastSelNode : false,
26076     
26077     
26078     getSelection : function() 
26079     {
26080         this.assignDocWin();
26081         return Roo.isIE ? this.doc.selection : this.win.getSelection();
26082     },
26083     
26084     getSelectedNode: function() 
26085     {
26086         // this may only work on Gecko!!!
26087         
26088         // should we cache this!!!!
26089         
26090         
26091         
26092          
26093         var range = this.createRange(this.getSelection()).cloneRange();
26094         
26095         if (Roo.isIE) {
26096             var parent = range.parentElement();
26097             while (true) {
26098                 var testRange = range.duplicate();
26099                 testRange.moveToElementText(parent);
26100                 if (testRange.inRange(range)) {
26101                     break;
26102                 }
26103                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26104                     break;
26105                 }
26106                 parent = parent.parentElement;
26107             }
26108             return parent;
26109         }
26110         
26111         // is ancestor a text element.
26112         var ac =  range.commonAncestorContainer;
26113         if (ac.nodeType == 3) {
26114             ac = ac.parentNode;
26115         }
26116         
26117         var ar = ac.childNodes;
26118          
26119         var nodes = [];
26120         var other_nodes = [];
26121         var has_other_nodes = false;
26122         for (var i=0;i<ar.length;i++) {
26123             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
26124                 continue;
26125             }
26126             // fullly contained node.
26127             
26128             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26129                 nodes.push(ar[i]);
26130                 continue;
26131             }
26132             
26133             // probably selected..
26134             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26135                 other_nodes.push(ar[i]);
26136                 continue;
26137             }
26138             // outer..
26139             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
26140                 continue;
26141             }
26142             
26143             
26144             has_other_nodes = true;
26145         }
26146         if (!nodes.length && other_nodes.length) {
26147             nodes= other_nodes;
26148         }
26149         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26150             return false;
26151         }
26152         
26153         return nodes[0];
26154     },
26155     createRange: function(sel)
26156     {
26157         // this has strange effects when using with 
26158         // top toolbar - not sure if it's a great idea.
26159         //this.editor.contentWindow.focus();
26160         if (typeof sel != "undefined") {
26161             try {
26162                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26163             } catch(e) {
26164                 return this.doc.createRange();
26165             }
26166         } else {
26167             return this.doc.createRange();
26168         }
26169     },
26170     getParentElement: function()
26171     {
26172         
26173         this.assignDocWin();
26174         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26175         
26176         var range = this.createRange(sel);
26177          
26178         try {
26179             var p = range.commonAncestorContainer;
26180             while (p.nodeType == 3) { // text node
26181                 p = p.parentNode;
26182             }
26183             return p;
26184         } catch (e) {
26185             return null;
26186         }
26187     
26188     },
26189     /***
26190      *
26191      * Range intersection.. the hard stuff...
26192      *  '-1' = before
26193      *  '0' = hits..
26194      *  '1' = after.
26195      *         [ -- selected range --- ]
26196      *   [fail]                        [fail]
26197      *
26198      *    basically..
26199      *      if end is before start or  hits it. fail.
26200      *      if start is after end or hits it fail.
26201      *
26202      *   if either hits (but other is outside. - then it's not 
26203      *   
26204      *    
26205      **/
26206     
26207     
26208     // @see http://www.thismuchiknow.co.uk/?p=64.
26209     rangeIntersectsNode : function(range, node)
26210     {
26211         var nodeRange = node.ownerDocument.createRange();
26212         try {
26213             nodeRange.selectNode(node);
26214         } catch (e) {
26215             nodeRange.selectNodeContents(node);
26216         }
26217     
26218         var rangeStartRange = range.cloneRange();
26219         rangeStartRange.collapse(true);
26220     
26221         var rangeEndRange = range.cloneRange();
26222         rangeEndRange.collapse(false);
26223     
26224         var nodeStartRange = nodeRange.cloneRange();
26225         nodeStartRange.collapse(true);
26226     
26227         var nodeEndRange = nodeRange.cloneRange();
26228         nodeEndRange.collapse(false);
26229     
26230         return rangeStartRange.compareBoundaryPoints(
26231                  Range.START_TO_START, nodeEndRange) == -1 &&
26232                rangeEndRange.compareBoundaryPoints(
26233                  Range.START_TO_START, nodeStartRange) == 1;
26234         
26235          
26236     },
26237     rangeCompareNode : function(range, node)
26238     {
26239         var nodeRange = node.ownerDocument.createRange();
26240         try {
26241             nodeRange.selectNode(node);
26242         } catch (e) {
26243             nodeRange.selectNodeContents(node);
26244         }
26245         
26246         
26247         range.collapse(true);
26248     
26249         nodeRange.collapse(true);
26250      
26251         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26252         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
26253          
26254         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26255         
26256         var nodeIsBefore   =  ss == 1;
26257         var nodeIsAfter    = ee == -1;
26258         
26259         if (nodeIsBefore && nodeIsAfter)
26260             return 0; // outer
26261         if (!nodeIsBefore && nodeIsAfter)
26262             return 1; //right trailed.
26263         
26264         if (nodeIsBefore && !nodeIsAfter)
26265             return 2;  // left trailed.
26266         // fully contined.
26267         return 3;
26268     },
26269
26270     // private? - in a new class?
26271     cleanUpPaste :  function()
26272     {
26273         // cleans up the whole document..
26274          Roo.log('cleanuppaste');
26275         this.cleanUpChildren(this.doc.body);
26276         var clean = this.cleanWordChars(this.doc.body.innerHTML);
26277         if (clean != this.doc.body.innerHTML) {
26278             this.doc.body.innerHTML = clean;
26279         }
26280         
26281     },
26282     
26283     cleanWordChars : function(input) {// change the chars to hex code
26284         var he = Roo.form.HtmlEditor;
26285         
26286         var output = input;
26287         Roo.each(he.swapCodes, function(sw) { 
26288             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26289             
26290             output = output.replace(swapper, sw[1]);
26291         });
26292         
26293         return output;
26294     },
26295     
26296     
26297     cleanUpChildren : function (n)
26298     {
26299         if (!n.childNodes.length) {
26300             return;
26301         }
26302         for (var i = n.childNodes.length-1; i > -1 ; i--) {
26303            this.cleanUpChild(n.childNodes[i]);
26304         }
26305     },
26306     
26307     
26308         
26309     
26310     cleanUpChild : function (node)
26311     {
26312         var ed = this;
26313         //console.log(node);
26314         if (node.nodeName == "#text") {
26315             // clean up silly Windows -- stuff?
26316             return; 
26317         }
26318         if (node.nodeName == "#comment") {
26319             node.parentNode.removeChild(node);
26320             // clean up silly Windows -- stuff?
26321             return; 
26322         }
26323         
26324         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
26325             // remove node.
26326             node.parentNode.removeChild(node);
26327             return;
26328             
26329         }
26330         
26331         var remove_keep_children= Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1;
26332         
26333         // remove <a name=....> as rendering on yahoo mailer is borked with this.
26334         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26335         
26336         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26337         //    remove_keep_children = true;
26338         //}
26339         
26340         if (remove_keep_children) {
26341             this.cleanUpChildren(node);
26342             // inserts everything just before this node...
26343             while (node.childNodes.length) {
26344                 var cn = node.childNodes[0];
26345                 node.removeChild(cn);
26346                 node.parentNode.insertBefore(cn, node);
26347             }
26348             node.parentNode.removeChild(node);
26349             return;
26350         }
26351         
26352         if (!node.attributes || !node.attributes.length) {
26353             this.cleanUpChildren(node);
26354             return;
26355         }
26356         
26357         function cleanAttr(n,v)
26358         {
26359             
26360             if (v.match(/^\./) || v.match(/^\//)) {
26361                 return;
26362             }
26363             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
26364                 return;
26365             }
26366             if (v.match(/^#/)) {
26367                 return;
26368             }
26369 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26370             node.removeAttribute(n);
26371             
26372         }
26373         
26374         function cleanStyle(n,v)
26375         {
26376             if (v.match(/expression/)) { //XSS?? should we even bother..
26377                 node.removeAttribute(n);
26378                 return;
26379             }
26380             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.form.HtmlEditor.cwhite : ed.cwhite;
26381             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.form.HtmlEditor.cblack : ed.cblack;
26382             
26383             
26384             var parts = v.split(/;/);
26385             var clean = [];
26386             
26387             Roo.each(parts, function(p) {
26388                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26389                 if (!p.length) {
26390                     return true;
26391                 }
26392                 var l = p.split(':').shift().replace(/\s+/g,'');
26393                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26394                 
26395                 
26396                 if ( cblack.indexOf(l) > -1) {
26397 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26398                     //node.removeAttribute(n);
26399                     return true;
26400                 }
26401                 //Roo.log()
26402                 // only allow 'c whitelisted system attributes'
26403                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
26404 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26405                     //node.removeAttribute(n);
26406                     return true;
26407                 }
26408                 
26409                 
26410                  
26411                 
26412                 clean.push(p);
26413                 return true;
26414             });
26415             if (clean.length) { 
26416                 node.setAttribute(n, clean.join(';'));
26417             } else {
26418                 node.removeAttribute(n);
26419             }
26420             
26421         }
26422         
26423         
26424         for (var i = node.attributes.length-1; i > -1 ; i--) {
26425             var a = node.attributes[i];
26426             //console.log(a);
26427             
26428             if (a.name.toLowerCase().substr(0,2)=='on')  {
26429                 node.removeAttribute(a.name);
26430                 continue;
26431             }
26432             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
26433                 node.removeAttribute(a.name);
26434                 continue;
26435             }
26436             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
26437                 cleanAttr(a.name,a.value); // fixme..
26438                 continue;
26439             }
26440             if (a.name == 'style') {
26441                 cleanStyle(a.name,a.value);
26442                 continue;
26443             }
26444             /// clean up MS crap..
26445             // tecnically this should be a list of valid class'es..
26446             
26447             
26448             if (a.name == 'class') {
26449                 if (a.value.match(/^Mso/)) {
26450                     node.className = '';
26451                 }
26452                 
26453                 if (a.value.match(/body/)) {
26454                     node.className = '';
26455                 }
26456                 continue;
26457             }
26458             
26459             // style cleanup!?
26460             // class cleanup?
26461             
26462         }
26463         
26464         
26465         this.cleanUpChildren(node);
26466         
26467         
26468     }
26469     
26470     
26471     // hide stuff that is not compatible
26472     /**
26473      * @event blur
26474      * @hide
26475      */
26476     /**
26477      * @event change
26478      * @hide
26479      */
26480     /**
26481      * @event focus
26482      * @hide
26483      */
26484     /**
26485      * @event specialkey
26486      * @hide
26487      */
26488     /**
26489      * @cfg {String} fieldClass @hide
26490      */
26491     /**
26492      * @cfg {String} focusClass @hide
26493      */
26494     /**
26495      * @cfg {String} autoCreate @hide
26496      */
26497     /**
26498      * @cfg {String} inputType @hide
26499      */
26500     /**
26501      * @cfg {String} invalidClass @hide
26502      */
26503     /**
26504      * @cfg {String} invalidText @hide
26505      */
26506     /**
26507      * @cfg {String} msgFx @hide
26508      */
26509     /**
26510      * @cfg {String} validateOnBlur @hide
26511      */
26512 });
26513
26514 Roo.form.HtmlEditor.white = [
26515         'area', 'br', 'img', 'input', 'hr', 'wbr',
26516         
26517        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
26518        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
26519        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
26520        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
26521        'table',   'ul',         'xmp', 
26522        
26523        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
26524       'thead',   'tr', 
26525      
26526       'dir', 'menu', 'ol', 'ul', 'dl',
26527        
26528       'embed',  'object'
26529 ];
26530
26531
26532 Roo.form.HtmlEditor.black = [
26533     //    'embed',  'object', // enable - backend responsiblity to clean thiese
26534         'applet', // 
26535         'base',   'basefont', 'bgsound', 'blink',  'body', 
26536         'frame',  'frameset', 'head',    'html',   'ilayer', 
26537         'iframe', 'layer',  'link',     'meta',    'object',   
26538         'script', 'style' ,'title',  'xml' // clean later..
26539 ];
26540 Roo.form.HtmlEditor.clean = [
26541     'script', 'style', 'title', 'xml'
26542 ];
26543 Roo.form.HtmlEditor.remove = [
26544     'font'
26545 ];
26546 // attributes..
26547
26548 Roo.form.HtmlEditor.ablack = [
26549     'on'
26550 ];
26551     
26552 Roo.form.HtmlEditor.aclean = [ 
26553     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
26554 ];
26555
26556 // protocols..
26557 Roo.form.HtmlEditor.pwhite= [
26558         'http',  'https',  'mailto'
26559 ];
26560
26561 // white listed style attributes.
26562 Roo.form.HtmlEditor.cwhite= [
26563       //  'text-align', /// default is to allow most things..
26564       
26565          
26566 //        'font-size'//??
26567 ];
26568
26569 // black listed style attributes.
26570 Roo.form.HtmlEditor.cblack= [
26571       //  'font-size' -- this can be set by the project 
26572 ];
26573
26574
26575 Roo.form.HtmlEditor.swapCodes   =[ 
26576     [    8211, "--" ], 
26577     [    8212, "--" ], 
26578     [    8216,  "'" ],  
26579     [    8217, "'" ],  
26580     [    8220, '"' ],  
26581     [    8221, '"' ],  
26582     [    8226, "*" ],  
26583     [    8230, "..." ]
26584 ]; 
26585
26586     // <script type="text/javascript">
26587 /*
26588  * Based on
26589  * Ext JS Library 1.1.1
26590  * Copyright(c) 2006-2007, Ext JS, LLC.
26591  *  
26592  
26593  */
26594
26595 /**
26596  * @class Roo.form.HtmlEditorToolbar1
26597  * Basic Toolbar
26598  * 
26599  * Usage:
26600  *
26601  new Roo.form.HtmlEditor({
26602     ....
26603     toolbars : [
26604         new Roo.form.HtmlEditorToolbar1({
26605             disable : { fonts: 1 , format: 1, ..., ... , ...],
26606             btns : [ .... ]
26607         })
26608     }
26609      
26610  * 
26611  * @cfg {Object} disable List of elements to disable..
26612  * @cfg {Array} btns List of additional buttons.
26613  * 
26614  * 
26615  * NEEDS Extra CSS? 
26616  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26617  */
26618  
26619 Roo.form.HtmlEditor.ToolbarStandard = function(config)
26620 {
26621     
26622     Roo.apply(this, config);
26623     
26624     // default disabled, based on 'good practice'..
26625     this.disable = this.disable || {};
26626     Roo.applyIf(this.disable, {
26627         fontSize : true,
26628         colors : true,
26629         specialElements : true
26630     });
26631     
26632     
26633     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26634     // dont call parent... till later.
26635 }
26636
26637 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
26638     
26639     tb: false,
26640     
26641     rendered: false,
26642     
26643     editor : false,
26644     /**
26645      * @cfg {Object} disable  List of toolbar elements to disable
26646          
26647      */
26648     disable : false,
26649       /**
26650      * @cfg {Array} fontFamilies An array of available font families
26651      */
26652     fontFamilies : [
26653         'Arial',
26654         'Courier New',
26655         'Tahoma',
26656         'Times New Roman',
26657         'Verdana'
26658     ],
26659     
26660     specialChars : [
26661            "&#169;",
26662           "&#174;",     
26663           "&#8482;",    
26664           "&#163;" ,    
26665          // "&#8212;",    
26666           "&#8230;",    
26667           "&#247;" ,    
26668         //  "&#225;" ,     ?? a acute?
26669            "&#8364;"    , //Euro
26670        //   "&#8220;"    ,
26671         //  "&#8221;"    ,
26672         //  "&#8226;"    ,
26673           "&#176;"  //   , // degrees
26674
26675          // "&#233;"     , // e ecute
26676          // "&#250;"     , // u ecute?
26677     ],
26678     
26679     specialElements : [
26680         {
26681             text: "Insert Table",
26682             xtype: 'MenuItem',
26683             xns : Roo.Menu,
26684             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
26685                 
26686         },
26687         {    
26688             text: "Insert Image",
26689             xtype: 'MenuItem',
26690             xns : Roo.Menu,
26691             ihtml : '<img src="about:blank"/>'
26692             
26693         }
26694         
26695          
26696     ],
26697     
26698     
26699     inputElements : [ 
26700             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
26701             "input:submit", "input:button", "select", "textarea", "label" ],
26702     formats : [
26703         ["p"] ,  
26704         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
26705         ["pre"],[ "code"], 
26706         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
26707         ['div'],['span']
26708     ],
26709      /**
26710      * @cfg {String} defaultFont default font to use.
26711      */
26712     defaultFont: 'tahoma',
26713    
26714     fontSelect : false,
26715     
26716     
26717     formatCombo : false,
26718     
26719     init : function(editor)
26720     {
26721         this.editor = editor;
26722         
26723         
26724         var fid = editor.frameId;
26725         var etb = this;
26726         function btn(id, toggle, handler){
26727             var xid = fid + '-'+ id ;
26728             return {
26729                 id : xid,
26730                 cmd : id,
26731                 cls : 'x-btn-icon x-edit-'+id,
26732                 enableToggle:toggle !== false,
26733                 scope: editor, // was editor...
26734                 handler:handler||editor.relayBtnCmd,
26735                 clickEvent:'mousedown',
26736                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
26737                 tabIndex:-1
26738             };
26739         }
26740         
26741         
26742         
26743         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
26744         this.tb = tb;
26745          // stop form submits
26746         tb.el.on('click', function(e){
26747             e.preventDefault(); // what does this do?
26748         });
26749
26750         if(!this.disable.font) { // && !Roo.isSafari){
26751             /* why no safari for fonts 
26752             editor.fontSelect = tb.el.createChild({
26753                 tag:'select',
26754                 tabIndex: -1,
26755                 cls:'x-font-select',
26756                 html: this.createFontOptions()
26757             });
26758             
26759             editor.fontSelect.on('change', function(){
26760                 var font = editor.fontSelect.dom.value;
26761                 editor.relayCmd('fontname', font);
26762                 editor.deferFocus();
26763             }, editor);
26764             
26765             tb.add(
26766                 editor.fontSelect.dom,
26767                 '-'
26768             );
26769             */
26770             
26771         };
26772         if(!this.disable.formats){
26773             this.formatCombo = new Roo.form.ComboBox({
26774                 store: new Roo.data.SimpleStore({
26775                     id : 'tag',
26776                     fields: ['tag'],
26777                     data : this.formats // from states.js
26778                 }),
26779                 blockFocus : true,
26780                 name : '',
26781                 //autoCreate : {tag: "div",  size: "20"},
26782                 displayField:'tag',
26783                 typeAhead: false,
26784                 mode: 'local',
26785                 editable : false,
26786                 triggerAction: 'all',
26787                 emptyText:'Add tag',
26788                 selectOnFocus:true,
26789                 width:135,
26790                 listeners : {
26791                     'select': function(c, r, i) {
26792                         editor.insertTag(r.get('tag'));
26793                         editor.focus();
26794                     }
26795                 }
26796
26797             });
26798             tb.addField(this.formatCombo);
26799             
26800         }
26801         
26802         if(!this.disable.format){
26803             tb.add(
26804                 btn('bold'),
26805                 btn('italic'),
26806                 btn('underline')
26807             );
26808         };
26809         if(!this.disable.fontSize){
26810             tb.add(
26811                 '-',
26812                 
26813                 
26814                 btn('increasefontsize', false, editor.adjustFont),
26815                 btn('decreasefontsize', false, editor.adjustFont)
26816             );
26817         };
26818         
26819         
26820         if(!this.disable.colors){
26821             tb.add(
26822                 '-', {
26823                     id:editor.frameId +'-forecolor',
26824                     cls:'x-btn-icon x-edit-forecolor',
26825                     clickEvent:'mousedown',
26826                     tooltip: this.buttonTips['forecolor'] || undefined,
26827                     tabIndex:-1,
26828                     menu : new Roo.menu.ColorMenu({
26829                         allowReselect: true,
26830                         focus: Roo.emptyFn,
26831                         value:'000000',
26832                         plain:true,
26833                         selectHandler: function(cp, color){
26834                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
26835                             editor.deferFocus();
26836                         },
26837                         scope: editor,
26838                         clickEvent:'mousedown'
26839                     })
26840                 }, {
26841                     id:editor.frameId +'backcolor',
26842                     cls:'x-btn-icon x-edit-backcolor',
26843                     clickEvent:'mousedown',
26844                     tooltip: this.buttonTips['backcolor'] || undefined,
26845                     tabIndex:-1,
26846                     menu : new Roo.menu.ColorMenu({
26847                         focus: Roo.emptyFn,
26848                         value:'FFFFFF',
26849                         plain:true,
26850                         allowReselect: true,
26851                         selectHandler: function(cp, color){
26852                             if(Roo.isGecko){
26853                                 editor.execCmd('useCSS', false);
26854                                 editor.execCmd('hilitecolor', color);
26855                                 editor.execCmd('useCSS', true);
26856                                 editor.deferFocus();
26857                             }else{
26858                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
26859                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
26860                                 editor.deferFocus();
26861                             }
26862                         },
26863                         scope:editor,
26864                         clickEvent:'mousedown'
26865                     })
26866                 }
26867             );
26868         };
26869         // now add all the items...
26870         
26871
26872         if(!this.disable.alignments){
26873             tb.add(
26874                 '-',
26875                 btn('justifyleft'),
26876                 btn('justifycenter'),
26877                 btn('justifyright')
26878             );
26879         };
26880
26881         //if(!Roo.isSafari){
26882             if(!this.disable.links){
26883                 tb.add(
26884                     '-',
26885                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
26886                 );
26887             };
26888
26889             if(!this.disable.lists){
26890                 tb.add(
26891                     '-',
26892                     btn('insertorderedlist'),
26893                     btn('insertunorderedlist')
26894                 );
26895             }
26896             if(!this.disable.sourceEdit){
26897                 tb.add(
26898                     '-',
26899                     btn('sourceedit', true, function(btn){
26900                         this.toggleSourceEdit(btn.pressed);
26901                     })
26902                 );
26903             }
26904         //}
26905         
26906         var smenu = { };
26907         // special menu.. - needs to be tidied up..
26908         if (!this.disable.special) {
26909             smenu = {
26910                 text: "&#169;",
26911                 cls: 'x-edit-none',
26912                 
26913                 menu : {
26914                     items : []
26915                 }
26916             };
26917             for (var i =0; i < this.specialChars.length; i++) {
26918                 smenu.menu.items.push({
26919                     
26920                     html: this.specialChars[i],
26921                     handler: function(a,b) {
26922                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
26923                         //editor.insertAtCursor(a.html);
26924                         
26925                     },
26926                     tabIndex:-1
26927                 });
26928             }
26929             
26930             
26931             tb.add(smenu);
26932             
26933             
26934         }
26935          
26936         if (!this.disable.specialElements) {
26937             var semenu = {
26938                 text: "Other;",
26939                 cls: 'x-edit-none',
26940                 menu : {
26941                     items : []
26942                 }
26943             };
26944             for (var i =0; i < this.specialElements.length; i++) {
26945                 semenu.menu.items.push(
26946                     Roo.apply({ 
26947                         handler: function(a,b) {
26948                             editor.insertAtCursor(this.ihtml);
26949                         }
26950                     }, this.specialElements[i])
26951                 );
26952                     
26953             }
26954             
26955             tb.add(semenu);
26956             
26957             
26958         }
26959          
26960         
26961         if (this.btns) {
26962             for(var i =0; i< this.btns.length;i++) {
26963                 var b = Roo.factory(this.btns[i],Roo.form);
26964                 b.cls =  'x-edit-none';
26965                 b.scope = editor;
26966                 tb.add(b);
26967             }
26968         
26969         }
26970         
26971         
26972         
26973         // disable everything...
26974         
26975         this.tb.items.each(function(item){
26976            if(item.id != editor.frameId+ '-sourceedit'){
26977                 item.disable();
26978             }
26979         });
26980         this.rendered = true;
26981         
26982         // the all the btns;
26983         editor.on('editorevent', this.updateToolbar, this);
26984         // other toolbars need to implement this..
26985         //editor.on('editmodechange', this.updateToolbar, this);
26986     },
26987     
26988     
26989     
26990     /**
26991      * Protected method that will not generally be called directly. It triggers
26992      * a toolbar update by reading the markup state of the current selection in the editor.
26993      */
26994     updateToolbar: function(){
26995
26996         if(!this.editor.activated){
26997             this.editor.onFirstFocus();
26998             return;
26999         }
27000
27001         var btns = this.tb.items.map, 
27002             doc = this.editor.doc,
27003             frameId = this.editor.frameId;
27004
27005         if(!this.disable.font && !Roo.isSafari){
27006             /*
27007             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
27008             if(name != this.fontSelect.dom.value){
27009                 this.fontSelect.dom.value = name;
27010             }
27011             */
27012         }
27013         if(!this.disable.format){
27014             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
27015             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
27016             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
27017         }
27018         if(!this.disable.alignments){
27019             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
27020             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
27021             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
27022         }
27023         if(!Roo.isSafari && !this.disable.lists){
27024             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
27025             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
27026         }
27027         
27028         var ans = this.editor.getAllAncestors();
27029         if (this.formatCombo) {
27030             
27031             
27032             var store = this.formatCombo.store;
27033             this.formatCombo.setValue("");
27034             for (var i =0; i < ans.length;i++) {
27035                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27036                     // select it..
27037                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27038                     break;
27039                 }
27040             }
27041         }
27042         
27043         
27044         
27045         // hides menus... - so this cant be on a menu...
27046         Roo.menu.MenuMgr.hideAll();
27047
27048         //this.editorsyncValue();
27049     },
27050    
27051     
27052     createFontOptions : function(){
27053         var buf = [], fs = this.fontFamilies, ff, lc;
27054         
27055         
27056         
27057         for(var i = 0, len = fs.length; i< len; i++){
27058             ff = fs[i];
27059             lc = ff.toLowerCase();
27060             buf.push(
27061                 '<option value="',lc,'" style="font-family:',ff,';"',
27062                     (this.defaultFont == lc ? ' selected="true">' : '>'),
27063                     ff,
27064                 '</option>'
27065             );
27066         }
27067         return buf.join('');
27068     },
27069     
27070     toggleSourceEdit : function(sourceEditMode){
27071         if(sourceEditMode === undefined){
27072             sourceEditMode = !this.sourceEditMode;
27073         }
27074         this.sourceEditMode = sourceEditMode === true;
27075         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
27076         // just toggle the button?
27077         if(btn.pressed !== this.editor.sourceEditMode){
27078             btn.toggle(this.editor.sourceEditMode);
27079             return;
27080         }
27081         
27082         if(this.sourceEditMode){
27083             this.tb.items.each(function(item){
27084                 if(item.cmd != 'sourceedit'){
27085                     item.disable();
27086                 }
27087             });
27088           
27089         }else{
27090             if(this.initialized){
27091                 this.tb.items.each(function(item){
27092                     item.enable();
27093                 });
27094             }
27095             
27096         }
27097         // tell the editor that it's been pressed..
27098         this.editor.toggleSourceEdit(sourceEditMode);
27099        
27100     },
27101      /**
27102      * Object collection of toolbar tooltips for the buttons in the editor. The key
27103      * is the command id associated with that button and the value is a valid QuickTips object.
27104      * For example:
27105 <pre><code>
27106 {
27107     bold : {
27108         title: 'Bold (Ctrl+B)',
27109         text: 'Make the selected text bold.',
27110         cls: 'x-html-editor-tip'
27111     },
27112     italic : {
27113         title: 'Italic (Ctrl+I)',
27114         text: 'Make the selected text italic.',
27115         cls: 'x-html-editor-tip'
27116     },
27117     ...
27118 </code></pre>
27119     * @type Object
27120      */
27121     buttonTips : {
27122         bold : {
27123             title: 'Bold (Ctrl+B)',
27124             text: 'Make the selected text bold.',
27125             cls: 'x-html-editor-tip'
27126         },
27127         italic : {
27128             title: 'Italic (Ctrl+I)',
27129             text: 'Make the selected text italic.',
27130             cls: 'x-html-editor-tip'
27131         },
27132         underline : {
27133             title: 'Underline (Ctrl+U)',
27134             text: 'Underline the selected text.',
27135             cls: 'x-html-editor-tip'
27136         },
27137         increasefontsize : {
27138             title: 'Grow Text',
27139             text: 'Increase the font size.',
27140             cls: 'x-html-editor-tip'
27141         },
27142         decreasefontsize : {
27143             title: 'Shrink Text',
27144             text: 'Decrease the font size.',
27145             cls: 'x-html-editor-tip'
27146         },
27147         backcolor : {
27148             title: 'Text Highlight Color',
27149             text: 'Change the background color of the selected text.',
27150             cls: 'x-html-editor-tip'
27151         },
27152         forecolor : {
27153             title: 'Font Color',
27154             text: 'Change the color of the selected text.',
27155             cls: 'x-html-editor-tip'
27156         },
27157         justifyleft : {
27158             title: 'Align Text Left',
27159             text: 'Align text to the left.',
27160             cls: 'x-html-editor-tip'
27161         },
27162         justifycenter : {
27163             title: 'Center Text',
27164             text: 'Center text in the editor.',
27165             cls: 'x-html-editor-tip'
27166         },
27167         justifyright : {
27168             title: 'Align Text Right',
27169             text: 'Align text to the right.',
27170             cls: 'x-html-editor-tip'
27171         },
27172         insertunorderedlist : {
27173             title: 'Bullet List',
27174             text: 'Start a bulleted list.',
27175             cls: 'x-html-editor-tip'
27176         },
27177         insertorderedlist : {
27178             title: 'Numbered List',
27179             text: 'Start a numbered list.',
27180             cls: 'x-html-editor-tip'
27181         },
27182         createlink : {
27183             title: 'Hyperlink',
27184             text: 'Make the selected text a hyperlink.',
27185             cls: 'x-html-editor-tip'
27186         },
27187         sourceedit : {
27188             title: 'Source Edit',
27189             text: 'Switch to source editing mode.',
27190             cls: 'x-html-editor-tip'
27191         }
27192     },
27193     // private
27194     onDestroy : function(){
27195         if(this.rendered){
27196             
27197             this.tb.items.each(function(item){
27198                 if(item.menu){
27199                     item.menu.removeAll();
27200                     if(item.menu.el){
27201                         item.menu.el.destroy();
27202                     }
27203                 }
27204                 item.destroy();
27205             });
27206              
27207         }
27208     },
27209     onFirstFocus: function() {
27210         this.tb.items.each(function(item){
27211            item.enable();
27212         });
27213     }
27214 });
27215
27216
27217
27218
27219 // <script type="text/javascript">
27220 /*
27221  * Based on
27222  * Ext JS Library 1.1.1
27223  * Copyright(c) 2006-2007, Ext JS, LLC.
27224  *  
27225  
27226  */
27227
27228  
27229 /**
27230  * @class Roo.form.HtmlEditor.ToolbarContext
27231  * Context Toolbar
27232  * 
27233  * Usage:
27234  *
27235  new Roo.form.HtmlEditor({
27236     ....
27237     toolbars : [
27238         { xtype: 'ToolbarStandard', styles : {} }
27239         { xtype: 'ToolbarContext', disable : {} }
27240     ]
27241 })
27242
27243      
27244  * 
27245  * @config : {Object} disable List of elements to disable.. (not done yet.)
27246  * @config : {Object} styles  Map of styles available.
27247  * 
27248  */
27249
27250 Roo.form.HtmlEditor.ToolbarContext = function(config)
27251 {
27252     
27253     Roo.apply(this, config);
27254     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27255     // dont call parent... till later.
27256     this.styles = this.styles || {};
27257 }
27258
27259  
27260
27261 Roo.form.HtmlEditor.ToolbarContext.types = {
27262     'IMG' : {
27263         width : {
27264             title: "Width",
27265             width: 40
27266         },
27267         height:  {
27268             title: "Height",
27269             width: 40
27270         },
27271         align: {
27272             title: "Align",
27273             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
27274             width : 80
27275             
27276         },
27277         border: {
27278             title: "Border",
27279             width: 40
27280         },
27281         alt: {
27282             title: "Alt",
27283             width: 120
27284         },
27285         src : {
27286             title: "Src",
27287             width: 220
27288         }
27289         
27290     },
27291     'A' : {
27292         name : {
27293             title: "Name",
27294             width: 50
27295         },
27296         href:  {
27297             title: "Href",
27298             width: 220
27299         } // border?
27300         
27301     },
27302     'TABLE' : {
27303         rows : {
27304             title: "Rows",
27305             width: 20
27306         },
27307         cols : {
27308             title: "Cols",
27309             width: 20
27310         },
27311         width : {
27312             title: "Width",
27313             width: 40
27314         },
27315         height : {
27316             title: "Height",
27317             width: 40
27318         },
27319         border : {
27320             title: "Border",
27321             width: 20
27322         }
27323     },
27324     'TD' : {
27325         width : {
27326             title: "Width",
27327             width: 40
27328         },
27329         height : {
27330             title: "Height",
27331             width: 40
27332         },   
27333         align: {
27334             title: "Align",
27335             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
27336             width: 80
27337         },
27338         valign: {
27339             title: "Valign",
27340             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
27341             width: 80
27342         },
27343         colspan: {
27344             title: "Colspan",
27345             width: 20
27346             
27347         },
27348          'font-family'  : {
27349             title : "Font",
27350             style : 'fontFamily',
27351             displayField: 'display',
27352             optname : 'font-family',
27353             width: 140
27354         }
27355     },
27356     'INPUT' : {
27357         name : {
27358             title: "name",
27359             width: 120
27360         },
27361         value : {
27362             title: "Value",
27363             width: 120
27364         },
27365         width : {
27366             title: "Width",
27367             width: 40
27368         }
27369     },
27370     'LABEL' : {
27371         'for' : {
27372             title: "For",
27373             width: 120
27374         }
27375     },
27376     'TEXTAREA' : {
27377           name : {
27378             title: "name",
27379             width: 120
27380         },
27381         rows : {
27382             title: "Rows",
27383             width: 20
27384         },
27385         cols : {
27386             title: "Cols",
27387             width: 20
27388         }
27389     },
27390     'SELECT' : {
27391         name : {
27392             title: "name",
27393             width: 120
27394         },
27395         selectoptions : {
27396             title: "Options",
27397             width: 200
27398         }
27399     },
27400     
27401     // should we really allow this??
27402     // should this just be 
27403     'BODY' : {
27404         title : {
27405             title: "Title",
27406             width: 200,
27407             disabled : true
27408         }
27409     },
27410     'SPAN' : {
27411         'font-family'  : {
27412             title : "Font",
27413             style : 'fontFamily',
27414             displayField: 'display',
27415             optname : 'font-family',
27416             width: 140
27417         }
27418     },
27419     'DIV' : {
27420         'font-family'  : {
27421             title : "Font",
27422             style : 'fontFamily',
27423             displayField: 'display',
27424             optname : 'font-family',
27425             width: 140
27426         }
27427     },
27428      'P' : {
27429         'font-family'  : {
27430             title : "Font",
27431             style : 'fontFamily',
27432             displayField: 'display',
27433             optname : 'font-family',
27434             width: 140
27435         }
27436     },
27437     
27438     '*' : {
27439         // empty..
27440     }
27441
27442 };
27443
27444 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
27445 Roo.form.HtmlEditor.ToolbarContext.stores = false;
27446
27447 Roo.form.HtmlEditor.ToolbarContext.options = {
27448         'font-family'  : [ 
27449                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
27450                 [ 'Courier New', 'Courier New'],
27451                 [ 'Tahoma', 'Tahoma'],
27452                 [ 'Times New Roman,serif', 'Times'],
27453                 [ 'Verdana','Verdana' ]
27454         ]
27455 };
27456
27457 // fixme - these need to be configurable..
27458  
27459
27460 Roo.form.HtmlEditor.ToolbarContext.types
27461
27462
27463 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
27464     
27465     tb: false,
27466     
27467     rendered: false,
27468     
27469     editor : false,
27470     /**
27471      * @cfg {Object} disable  List of toolbar elements to disable
27472          
27473      */
27474     disable : false,
27475     /**
27476      * @cfg {Object} styles List of styles 
27477      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
27478      *
27479      * These must be defined in the page, so they get rendered correctly..
27480      * .headline { }
27481      * TD.underline { }
27482      * 
27483      */
27484     styles : false,
27485     
27486     options: false,
27487     
27488     toolbars : false,
27489     
27490     init : function(editor)
27491     {
27492         this.editor = editor;
27493         
27494         
27495         var fid = editor.frameId;
27496         var etb = this;
27497         function btn(id, toggle, handler){
27498             var xid = fid + '-'+ id ;
27499             return {
27500                 id : xid,
27501                 cmd : id,
27502                 cls : 'x-btn-icon x-edit-'+id,
27503                 enableToggle:toggle !== false,
27504                 scope: editor, // was editor...
27505                 handler:handler||editor.relayBtnCmd,
27506                 clickEvent:'mousedown',
27507                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27508                 tabIndex:-1
27509             };
27510         }
27511         // create a new element.
27512         var wdiv = editor.wrap.createChild({
27513                 tag: 'div'
27514             }, editor.wrap.dom.firstChild.nextSibling, true);
27515         
27516         // can we do this more than once??
27517         
27518          // stop form submits
27519       
27520  
27521         // disable everything...
27522         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
27523         this.toolbars = {};
27524            
27525         for (var i in  ty) {
27526           
27527             this.toolbars[i] = this.buildToolbar(ty[i],i);
27528         }
27529         this.tb = this.toolbars.BODY;
27530         this.tb.el.show();
27531         this.buildFooter();
27532         this.footer.show();
27533         editor.on('hide', function( ) { this.footer.hide() }, this);
27534         editor.on('show', function( ) { this.footer.show() }, this);
27535         
27536          
27537         this.rendered = true;
27538         
27539         // the all the btns;
27540         editor.on('editorevent', this.updateToolbar, this);
27541         // other toolbars need to implement this..
27542         //editor.on('editmodechange', this.updateToolbar, this);
27543     },
27544     
27545     
27546     
27547     /**
27548      * Protected method that will not generally be called directly. It triggers
27549      * a toolbar update by reading the markup state of the current selection in the editor.
27550      */
27551     updateToolbar: function(editor,ev,sel){
27552
27553         //Roo.log(ev);
27554         // capture mouse up - this is handy for selecting images..
27555         // perhaps should go somewhere else...
27556         if(!this.editor.activated){
27557              this.editor.onFirstFocus();
27558             return;
27559         }
27560         
27561         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
27562         // selectNode - might want to handle IE?
27563         if (ev &&
27564             (ev.type == 'mouseup' || ev.type == 'click' ) &&
27565             ev.target && ev.target.tagName == 'IMG') {
27566             // they have click on an image...
27567             // let's see if we can change the selection...
27568             sel = ev.target;
27569          
27570               var nodeRange = sel.ownerDocument.createRange();
27571             try {
27572                 nodeRange.selectNode(sel);
27573             } catch (e) {
27574                 nodeRange.selectNodeContents(sel);
27575             }
27576             //nodeRange.collapse(true);
27577             var s = editor.win.getSelection();
27578             s.removeAllRanges();
27579             s.addRange(nodeRange);
27580         }  
27581         
27582       
27583         var updateFooter = sel ? false : true;
27584         
27585         
27586         var ans = this.editor.getAllAncestors();
27587         
27588         // pick
27589         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
27590         
27591         if (!sel) { 
27592             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
27593             sel = sel ? sel : this.editor.doc.body;
27594             sel = sel.tagName.length ? sel : this.editor.doc.body;
27595             
27596         }
27597         // pick a menu that exists..
27598         var tn = sel.tagName.toUpperCase();
27599         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
27600         
27601         tn = sel.tagName.toUpperCase();
27602         
27603         var lastSel = this.tb.selectedNode
27604         
27605         this.tb.selectedNode = sel;
27606         
27607         // if current menu does not match..
27608         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
27609                 
27610             this.tb.el.hide();
27611             ///console.log("show: " + tn);
27612             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
27613             this.tb.el.show();
27614             // update name
27615             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
27616             
27617             
27618             // update attributes
27619             if (this.tb.fields) {
27620                 this.tb.fields.each(function(e) {
27621                     if (e.stylename) {
27622                         e.setValue(sel.style[e.stylename]);
27623                         return;
27624                     } 
27625                    e.setValue(sel.getAttribute(e.attrname));
27626                 });
27627             }
27628             
27629             var hasStyles = false;
27630             for(var i in this.styles) {
27631                 hasStyles = true;
27632                 break;
27633             }
27634             
27635             // update styles
27636             if (hasStyles) { 
27637                 var st = this.tb.fields.item(0);
27638                 
27639                 st.store.removeAll();
27640                
27641                 
27642                 var cn = sel.className.split(/\s+/);
27643                 
27644                 var avs = [];
27645                 if (this.styles['*']) {
27646                     
27647                     Roo.each(this.styles['*'], function(v) {
27648                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
27649                     });
27650                 }
27651                 if (this.styles[tn]) { 
27652                     Roo.each(this.styles[tn], function(v) {
27653                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
27654                     });
27655                 }
27656                 
27657                 st.store.loadData(avs);
27658                 st.collapse();
27659                 st.setValue(cn);
27660             }
27661             // flag our selected Node.
27662             this.tb.selectedNode = sel;
27663            
27664            
27665             Roo.menu.MenuMgr.hideAll();
27666
27667         }
27668         
27669         if (!updateFooter) {
27670             //this.footDisp.dom.innerHTML = ''; 
27671             return;
27672         }
27673         // update the footer
27674         //
27675         var html = '';
27676         
27677         this.footerEls = ans.reverse();
27678         Roo.each(this.footerEls, function(a,i) {
27679             if (!a) { return; }
27680             html += html.length ? ' &gt; '  :  '';
27681             
27682             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
27683             
27684         });
27685        
27686         // 
27687         var sz = this.footDisp.up('td').getSize();
27688         this.footDisp.dom.style.width = (sz.width -10) + 'px';
27689         this.footDisp.dom.style.marginLeft = '5px';
27690         
27691         this.footDisp.dom.style.overflow = 'hidden';
27692         
27693         this.footDisp.dom.innerHTML = html;
27694             
27695         //this.editorsyncValue();
27696     },
27697      
27698     
27699    
27700        
27701     // private
27702     onDestroy : function(){
27703         if(this.rendered){
27704             
27705             this.tb.items.each(function(item){
27706                 if(item.menu){
27707                     item.menu.removeAll();
27708                     if(item.menu.el){
27709                         item.menu.el.destroy();
27710                     }
27711                 }
27712                 item.destroy();
27713             });
27714              
27715         }
27716     },
27717     onFirstFocus: function() {
27718         // need to do this for all the toolbars..
27719         this.tb.items.each(function(item){
27720            item.enable();
27721         });
27722     },
27723     buildToolbar: function(tlist, nm)
27724     {
27725         var editor = this.editor;
27726          // create a new element.
27727         var wdiv = editor.wrap.createChild({
27728                 tag: 'div'
27729             }, editor.wrap.dom.firstChild.nextSibling, true);
27730         
27731        
27732         var tb = new Roo.Toolbar(wdiv);
27733         // add the name..
27734         
27735         tb.add(nm+ ":&nbsp;");
27736         
27737         var styles = [];
27738         for(var i in this.styles) {
27739             styles.push(i);
27740         }
27741         
27742         // styles...
27743         if (styles && styles.length) {
27744             
27745             // this needs a multi-select checkbox...
27746             tb.addField( new Roo.form.ComboBox({
27747                 store: new Roo.data.SimpleStore({
27748                     id : 'val',
27749                     fields: ['val', 'selected'],
27750                     data : [] 
27751                 }),
27752                 name : '-roo-edit-className',
27753                 attrname : 'className',
27754                 displayField: 'val',
27755                 typeAhead: false,
27756                 mode: 'local',
27757                 editable : false,
27758                 triggerAction: 'all',
27759                 emptyText:'Select Style',
27760                 selectOnFocus:true,
27761                 width: 130,
27762                 listeners : {
27763                     'select': function(c, r, i) {
27764                         // initial support only for on class per el..
27765                         tb.selectedNode.className =  r ? r.get('val') : '';
27766                         editor.syncValue();
27767                     }
27768                 }
27769     
27770             }));
27771         }
27772         
27773         var tbc = Roo.form.HtmlEditor.ToolbarContext;
27774         var tbops = tbc.options;
27775         
27776         for (var i in tlist) {
27777             
27778             var item = tlist[i];
27779             tb.add(item.title + ":&nbsp;");
27780             
27781             
27782             //optname == used so you can configure the options available..
27783             var opts = item.opts ? item.opts : false;
27784             if (item.optname) {
27785                 opts = tbops[item.optname];
27786            
27787             }
27788             
27789             if (opts) {
27790                 // opts == pulldown..
27791                 tb.addField( new Roo.form.ComboBox({
27792                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
27793                         id : 'val',
27794                         fields: ['val', 'display'],
27795                         data : opts  
27796                     }),
27797                     name : '-roo-edit-' + i,
27798                     attrname : i,
27799                     stylename : item.style ? item.style : false,
27800                     displayField: item.displayField ? item.displayField : 'val',
27801                     valueField :  'val',
27802                     typeAhead: false,
27803                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
27804                     editable : false,
27805                     triggerAction: 'all',
27806                     emptyText:'Select',
27807                     selectOnFocus:true,
27808                     width: item.width ? item.width  : 130,
27809                     listeners : {
27810                         'select': function(c, r, i) {
27811                             if (c.stylename) {
27812                                 tb.selectedNode.style[c.stylename] =  r.get('val');
27813                                 return;
27814                             }
27815                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
27816                         }
27817                     }
27818
27819                 }));
27820                 continue;
27821                     
27822                  
27823                 
27824                 tb.addField( new Roo.form.TextField({
27825                     name: i,
27826                     width: 100,
27827                     //allowBlank:false,
27828                     value: ''
27829                 }));
27830                 continue;
27831             }
27832             tb.addField( new Roo.form.TextField({
27833                 name: '-roo-edit-' + i,
27834                 attrname : i,
27835                 
27836                 width: item.width,
27837                 //allowBlank:true,
27838                 value: '',
27839                 listeners: {
27840                     'change' : function(f, nv, ov) {
27841                         tb.selectedNode.setAttribute(f.attrname, nv);
27842                     }
27843                 }
27844             }));
27845              
27846         }
27847         tb.addFill();
27848         var _this = this;
27849         tb.addButton( {
27850             text: 'Remove Tag',
27851     
27852             listeners : {
27853                 click : function ()
27854                 {
27855                     // remove
27856                     // undo does not work.
27857                      
27858                     var sn = tb.selectedNode;
27859                     
27860                     var pn = sn.parentNode;
27861                     
27862                     var stn =  sn.childNodes[0];
27863                     var en = sn.childNodes[sn.childNodes.length - 1 ];
27864                     while (sn.childNodes.length) {
27865                         var node = sn.childNodes[0];
27866                         sn.removeChild(node);
27867                         //Roo.log(node);
27868                         pn.insertBefore(node, sn);
27869                         
27870                     }
27871                     pn.removeChild(sn);
27872                     var range = editor.createRange();
27873         
27874                     range.setStart(stn,0);
27875                     range.setEnd(en,0); //????
27876                     //range.selectNode(sel);
27877                     
27878                     
27879                     var selection = editor.getSelection();
27880                     selection.removeAllRanges();
27881                     selection.addRange(range);
27882                     
27883                     
27884                     
27885                     //_this.updateToolbar(null, null, pn);
27886                     _this.updateToolbar(null, null, null);
27887                     _this.footDisp.dom.innerHTML = ''; 
27888                 }
27889             }
27890             
27891                     
27892                 
27893             
27894         });
27895         
27896         
27897         tb.el.on('click', function(e){
27898             e.preventDefault(); // what does this do?
27899         });
27900         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
27901         tb.el.hide();
27902         tb.name = nm;
27903         // dont need to disable them... as they will get hidden
27904         return tb;
27905          
27906         
27907     },
27908     buildFooter : function()
27909     {
27910         
27911         var fel = this.editor.wrap.createChild();
27912         this.footer = new Roo.Toolbar(fel);
27913         // toolbar has scrolly on left / right?
27914         var footDisp= new Roo.Toolbar.Fill();
27915         var _t = this;
27916         this.footer.add(
27917             {
27918                 text : '&lt;',
27919                 xtype: 'Button',
27920                 handler : function() {
27921                     _t.footDisp.scrollTo('left',0,true)
27922                 }
27923             }
27924         );
27925         this.footer.add( footDisp );
27926         this.footer.add( 
27927             {
27928                 text : '&gt;',
27929                 xtype: 'Button',
27930                 handler : function() {
27931                     // no animation..
27932                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
27933                 }
27934             }
27935         );
27936         var fel = Roo.get(footDisp.el);
27937         fel.addClass('x-editor-context');
27938         this.footDispWrap = fel; 
27939         this.footDispWrap.overflow  = 'hidden';
27940         
27941         this.footDisp = fel.createChild();
27942         this.footDispWrap.on('click', this.onContextClick, this)
27943         
27944         
27945     },
27946     onContextClick : function (ev,dom)
27947     {
27948         ev.preventDefault();
27949         var  cn = dom.className;
27950         //Roo.log(cn);
27951         if (!cn.match(/x-ed-loc-/)) {
27952             return;
27953         }
27954         var n = cn.split('-').pop();
27955         var ans = this.footerEls;
27956         var sel = ans[n];
27957         
27958          // pick
27959         var range = this.editor.createRange();
27960         
27961         range.selectNodeContents(sel);
27962         //range.selectNode(sel);
27963         
27964         
27965         var selection = this.editor.getSelection();
27966         selection.removeAllRanges();
27967         selection.addRange(range);
27968         
27969         
27970         
27971         this.updateToolbar(null, null, sel);
27972         
27973         
27974     }
27975     
27976     
27977     
27978     
27979     
27980 });
27981
27982
27983
27984
27985
27986 /*
27987  * Based on:
27988  * Ext JS Library 1.1.1
27989  * Copyright(c) 2006-2007, Ext JS, LLC.
27990  *
27991  * Originally Released Under LGPL - original licence link has changed is not relivant.
27992  *
27993  * Fork - LGPL
27994  * <script type="text/javascript">
27995  */
27996  
27997 /**
27998  * @class Roo.form.BasicForm
27999  * @extends Roo.util.Observable
28000  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
28001  * @constructor
28002  * @param {String/HTMLElement/Roo.Element} el The form element or its id
28003  * @param {Object} config Configuration options
28004  */
28005 Roo.form.BasicForm = function(el, config){
28006     this.allItems = [];
28007     this.childForms = [];
28008     Roo.apply(this, config);
28009     /*
28010      * The Roo.form.Field items in this form.
28011      * @type MixedCollection
28012      */
28013      
28014      
28015     this.items = new Roo.util.MixedCollection(false, function(o){
28016         return o.id || (o.id = Roo.id());
28017     });
28018     this.addEvents({
28019         /**
28020          * @event beforeaction
28021          * Fires before any action is performed. Return false to cancel the action.
28022          * @param {Form} this
28023          * @param {Action} action The action to be performed
28024          */
28025         beforeaction: true,
28026         /**
28027          * @event actionfailed
28028          * Fires when an action fails.
28029          * @param {Form} this
28030          * @param {Action} action The action that failed
28031          */
28032         actionfailed : true,
28033         /**
28034          * @event actioncomplete
28035          * Fires when an action is completed.
28036          * @param {Form} this
28037          * @param {Action} action The action that completed
28038          */
28039         actioncomplete : true
28040     });
28041     if(el){
28042         this.initEl(el);
28043     }
28044     Roo.form.BasicForm.superclass.constructor.call(this);
28045 };
28046
28047 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
28048     /**
28049      * @cfg {String} method
28050      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
28051      */
28052     /**
28053      * @cfg {DataReader} reader
28054      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
28055      * This is optional as there is built-in support for processing JSON.
28056      */
28057     /**
28058      * @cfg {DataReader} errorReader
28059      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
28060      * This is completely optional as there is built-in support for processing JSON.
28061      */
28062     /**
28063      * @cfg {String} url
28064      * The URL to use for form actions if one isn't supplied in the action options.
28065      */
28066     /**
28067      * @cfg {Boolean} fileUpload
28068      * Set to true if this form is a file upload.
28069      */
28070      
28071     /**
28072      * @cfg {Object} baseParams
28073      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
28074      */
28075      /**
28076      
28077     /**
28078      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
28079      */
28080     timeout: 30,
28081
28082     // private
28083     activeAction : null,
28084
28085     /**
28086      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
28087      * or setValues() data instead of when the form was first created.
28088      */
28089     trackResetOnLoad : false,
28090     
28091     
28092     /**
28093      * childForms - used for multi-tab forms
28094      * @type {Array}
28095      */
28096     childForms : false,
28097     
28098     /**
28099      * allItems - full list of fields.
28100      * @type {Array}
28101      */
28102     allItems : false,
28103     
28104     /**
28105      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
28106      * element by passing it or its id or mask the form itself by passing in true.
28107      * @type Mixed
28108      */
28109     waitMsgTarget : false,
28110
28111     // private
28112     initEl : function(el){
28113         this.el = Roo.get(el);
28114         this.id = this.el.id || Roo.id();
28115         this.el.on('submit', this.onSubmit, this);
28116         this.el.addClass('x-form');
28117     },
28118
28119     // private
28120     onSubmit : function(e){
28121         e.stopEvent();
28122     },
28123
28124     /**
28125      * Returns true if client-side validation on the form is successful.
28126      * @return Boolean
28127      */
28128     isValid : function(){
28129         var valid = true;
28130         this.items.each(function(f){
28131            if(!f.validate()){
28132                valid = false;
28133            }
28134         });
28135         return valid;
28136     },
28137
28138     /**
28139      * Returns true if any fields in this form have changed since their original load.
28140      * @return Boolean
28141      */
28142     isDirty : function(){
28143         var dirty = false;
28144         this.items.each(function(f){
28145            if(f.isDirty()){
28146                dirty = true;
28147                return false;
28148            }
28149         });
28150         return dirty;
28151     },
28152
28153     /**
28154      * Performs a predefined action (submit or load) or custom actions you define on this form.
28155      * @param {String} actionName The name of the action type
28156      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
28157      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
28158      * accept other config options):
28159      * <pre>
28160 Property          Type             Description
28161 ----------------  ---------------  ----------------------------------------------------------------------------------
28162 url               String           The url for the action (defaults to the form's url)
28163 method            String           The form method to use (defaults to the form's method, or POST if not defined)
28164 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
28165 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
28166                                    validate the form on the client (defaults to false)
28167      * </pre>
28168      * @return {BasicForm} this
28169      */
28170     doAction : function(action, options){
28171         if(typeof action == 'string'){
28172             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
28173         }
28174         if(this.fireEvent('beforeaction', this, action) !== false){
28175             this.beforeAction(action);
28176             action.run.defer(100, action);
28177         }
28178         return this;
28179     },
28180
28181     /**
28182      * Shortcut to do a submit action.
28183      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28184      * @return {BasicForm} this
28185      */
28186     submit : function(options){
28187         this.doAction('submit', options);
28188         return this;
28189     },
28190
28191     /**
28192      * Shortcut to do a load action.
28193      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28194      * @return {BasicForm} this
28195      */
28196     load : function(options){
28197         this.doAction('load', options);
28198         return this;
28199     },
28200
28201     /**
28202      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
28203      * @param {Record} record The record to edit
28204      * @return {BasicForm} this
28205      */
28206     updateRecord : function(record){
28207         record.beginEdit();
28208         var fs = record.fields;
28209         fs.each(function(f){
28210             var field = this.findField(f.name);
28211             if(field){
28212                 record.set(f.name, field.getValue());
28213             }
28214         }, this);
28215         record.endEdit();
28216         return this;
28217     },
28218
28219     /**
28220      * Loads an Roo.data.Record into this form.
28221      * @param {Record} record The record to load
28222      * @return {BasicForm} this
28223      */
28224     loadRecord : function(record){
28225         this.setValues(record.data);
28226         return this;
28227     },
28228
28229     // private
28230     beforeAction : function(action){
28231         var o = action.options;
28232         
28233        
28234         if(this.waitMsgTarget === true){
28235             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
28236         }else if(this.waitMsgTarget){
28237             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
28238             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
28239         }else {
28240             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
28241         }
28242          
28243     },
28244
28245     // private
28246     afterAction : function(action, success){
28247         this.activeAction = null;
28248         var o = action.options;
28249         
28250         if(this.waitMsgTarget === true){
28251             this.el.unmask();
28252         }else if(this.waitMsgTarget){
28253             this.waitMsgTarget.unmask();
28254         }else{
28255             Roo.MessageBox.updateProgress(1);
28256             Roo.MessageBox.hide();
28257         }
28258          
28259         if(success){
28260             if(o.reset){
28261                 this.reset();
28262             }
28263             Roo.callback(o.success, o.scope, [this, action]);
28264             this.fireEvent('actioncomplete', this, action);
28265             
28266         }else{
28267             
28268             // failure condition..
28269             // we have a scenario where updates need confirming.
28270             // eg. if a locking scenario exists..
28271             // we look for { errors : { needs_confirm : true }} in the response.
28272             if (
28273                 (typeof(action.result) != 'undefined')  &&
28274                 (typeof(action.result.errors) != 'undefined')  &&
28275                 (typeof(action.result.errors.needs_confirm) != 'undefined')
28276            ){
28277                 var _t = this;
28278                 Roo.MessageBox.confirm(
28279                     "Change requires confirmation",
28280                     action.result.errorMsg,
28281                     function(r) {
28282                         if (r != 'yes') {
28283                             return;
28284                         }
28285                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
28286                     }
28287                     
28288                 );
28289                 
28290                 
28291                 
28292                 return;
28293             }
28294             
28295             Roo.callback(o.failure, o.scope, [this, action]);
28296             // show an error message if no failed handler is set..
28297             if (!this.hasListener('actionfailed')) {
28298                 Roo.MessageBox.alert("Error",
28299                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
28300                         action.result.errorMsg :
28301                         "Saving Failed, please check your entries or try again"
28302                 );
28303             }
28304             
28305             this.fireEvent('actionfailed', this, action);
28306         }
28307         
28308     },
28309
28310     /**
28311      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
28312      * @param {String} id The value to search for
28313      * @return Field
28314      */
28315     findField : function(id){
28316         var field = this.items.get(id);
28317         if(!field){
28318             this.items.each(function(f){
28319                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
28320                     field = f;
28321                     return false;
28322                 }
28323             });
28324         }
28325         return field || null;
28326     },
28327
28328     /**
28329      * Add a secondary form to this one, 
28330      * Used to provide tabbed forms. One form is primary, with hidden values 
28331      * which mirror the elements from the other forms.
28332      * 
28333      * @param {Roo.form.Form} form to add.
28334      * 
28335      */
28336     addForm : function(form)
28337     {
28338        
28339         if (this.childForms.indexOf(form) > -1) {
28340             // already added..
28341             return;
28342         }
28343         this.childForms.push(form);
28344         var n = '';
28345         Roo.each(form.allItems, function (fe) {
28346             
28347             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
28348             if (this.findField(n)) { // already added..
28349                 return;
28350             }
28351             var add = new Roo.form.Hidden({
28352                 name : n
28353             });
28354             add.render(this.el);
28355             
28356             this.add( add );
28357         }, this);
28358         
28359     },
28360     /**
28361      * Mark fields in this form invalid in bulk.
28362      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
28363      * @return {BasicForm} this
28364      */
28365     markInvalid : function(errors){
28366         if(errors instanceof Array){
28367             for(var i = 0, len = errors.length; i < len; i++){
28368                 var fieldError = errors[i];
28369                 var f = this.findField(fieldError.id);
28370                 if(f){
28371                     f.markInvalid(fieldError.msg);
28372                 }
28373             }
28374         }else{
28375             var field, id;
28376             for(id in errors){
28377                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
28378                     field.markInvalid(errors[id]);
28379                 }
28380             }
28381         }
28382         Roo.each(this.childForms || [], function (f) {
28383             f.markInvalid(errors);
28384         });
28385         
28386         return this;
28387     },
28388
28389     /**
28390      * Set values for fields in this form in bulk.
28391      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
28392      * @return {BasicForm} this
28393      */
28394     setValues : function(values){
28395         if(values instanceof Array){ // array of objects
28396             for(var i = 0, len = values.length; i < len; i++){
28397                 var v = values[i];
28398                 var f = this.findField(v.id);
28399                 if(f){
28400                     f.setValue(v.value);
28401                     if(this.trackResetOnLoad){
28402                         f.originalValue = f.getValue();
28403                     }
28404                 }
28405             }
28406         }else{ // object hash
28407             var field, id;
28408             for(id in values){
28409                 if(typeof values[id] != 'function' && (field = this.findField(id))){
28410                     
28411                     if (field.setFromData && 
28412                         field.valueField && 
28413                         field.displayField &&
28414                         // combos' with local stores can 
28415                         // be queried via setValue()
28416                         // to set their value..
28417                         (field.store && !field.store.isLocal)
28418                         ) {
28419                         // it's a combo
28420                         var sd = { };
28421                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
28422                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
28423                         field.setFromData(sd);
28424                         
28425                     } else {
28426                         field.setValue(values[id]);
28427                     }
28428                     
28429                     
28430                     if(this.trackResetOnLoad){
28431                         field.originalValue = field.getValue();
28432                     }
28433                 }
28434             }
28435         }
28436          
28437         Roo.each(this.childForms || [], function (f) {
28438             f.setValues(values);
28439         });
28440                 
28441         return this;
28442     },
28443
28444     /**
28445      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
28446      * they are returned as an array.
28447      * @param {Boolean} asString
28448      * @return {Object}
28449      */
28450     getValues : function(asString){
28451         if (this.childForms) {
28452             // copy values from the child forms
28453             Roo.each(this.childForms, function (f) {
28454                 this.setValues(f.getValues());
28455             }, this);
28456         }
28457         
28458         
28459         
28460         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
28461         if(asString === true){
28462             return fs;
28463         }
28464         return Roo.urlDecode(fs);
28465     },
28466     
28467     /**
28468      * Returns the fields in this form as an object with key/value pairs. 
28469      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
28470      * @return {Object}
28471      */
28472     getFieldValues : function(with_hidden)
28473     {
28474         if (this.childForms) {
28475             // copy values from the child forms
28476             // should this call getFieldValues - probably not as we do not currently copy
28477             // hidden fields when we generate..
28478             Roo.each(this.childForms, function (f) {
28479                 this.setValues(f.getValues());
28480             }, this);
28481         }
28482         
28483         var ret = {};
28484         this.items.each(function(f){
28485             if (!f.getName()) {
28486                 return;
28487             }
28488             var v = f.getValue();
28489             if (f.inputType =='radio') {
28490                 if (typeof(ret[f.getName()]) == 'undefined') {
28491                     ret[f.getName()] = ''; // empty..
28492                 }
28493                 
28494                 if (!f.el.dom.checked) {
28495                     return;
28496                     
28497                 }
28498                 v = f.el.dom.value;
28499                 
28500             }
28501             
28502             // not sure if this supported any more..
28503             if ((typeof(v) == 'object') && f.getRawValue) {
28504                 v = f.getRawValue() ; // dates..
28505             }
28506             // combo boxes where name != hiddenName...
28507             if (f.name != f.getName()) {
28508                 ret[f.name] = f.getRawValue();
28509             }
28510             ret[f.getName()] = v;
28511         });
28512         
28513         return ret;
28514     },
28515
28516     /**
28517      * Clears all invalid messages in this form.
28518      * @return {BasicForm} this
28519      */
28520     clearInvalid : function(){
28521         this.items.each(function(f){
28522            f.clearInvalid();
28523         });
28524         
28525         Roo.each(this.childForms || [], function (f) {
28526             f.clearInvalid();
28527         });
28528         
28529         
28530         return this;
28531     },
28532
28533     /**
28534      * Resets this form.
28535      * @return {BasicForm} this
28536      */
28537     reset : function(){
28538         this.items.each(function(f){
28539             f.reset();
28540         });
28541         
28542         Roo.each(this.childForms || [], function (f) {
28543             f.reset();
28544         });
28545        
28546         
28547         return this;
28548     },
28549
28550     /**
28551      * Add Roo.form components to this form.
28552      * @param {Field} field1
28553      * @param {Field} field2 (optional)
28554      * @param {Field} etc (optional)
28555      * @return {BasicForm} this
28556      */
28557     add : function(){
28558         this.items.addAll(Array.prototype.slice.call(arguments, 0));
28559         return this;
28560     },
28561
28562
28563     /**
28564      * Removes a field from the items collection (does NOT remove its markup).
28565      * @param {Field} field
28566      * @return {BasicForm} this
28567      */
28568     remove : function(field){
28569         this.items.remove(field);
28570         return this;
28571     },
28572
28573     /**
28574      * Looks at the fields in this form, checks them for an id attribute,
28575      * and calls applyTo on the existing dom element with that id.
28576      * @return {BasicForm} this
28577      */
28578     render : function(){
28579         this.items.each(function(f){
28580             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
28581                 f.applyTo(f.id);
28582             }
28583         });
28584         return this;
28585     },
28586
28587     /**
28588      * Calls {@link Ext#apply} for all fields in this form with the passed object.
28589      * @param {Object} values
28590      * @return {BasicForm} this
28591      */
28592     applyToFields : function(o){
28593         this.items.each(function(f){
28594            Roo.apply(f, o);
28595         });
28596         return this;
28597     },
28598
28599     /**
28600      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
28601      * @param {Object} values
28602      * @return {BasicForm} this
28603      */
28604     applyIfToFields : function(o){
28605         this.items.each(function(f){
28606            Roo.applyIf(f, o);
28607         });
28608         return this;
28609     }
28610 });
28611
28612 // back compat
28613 Roo.BasicForm = Roo.form.BasicForm;/*
28614  * Based on:
28615  * Ext JS Library 1.1.1
28616  * Copyright(c) 2006-2007, Ext JS, LLC.
28617  *
28618  * Originally Released Under LGPL - original licence link has changed is not relivant.
28619  *
28620  * Fork - LGPL
28621  * <script type="text/javascript">
28622  */
28623
28624 /**
28625  * @class Roo.form.Form
28626  * @extends Roo.form.BasicForm
28627  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
28628  * @constructor
28629  * @param {Object} config Configuration options
28630  */
28631 Roo.form.Form = function(config){
28632     var xitems =  [];
28633     if (config.items) {
28634         xitems = config.items;
28635         delete config.items;
28636     }
28637    
28638     
28639     Roo.form.Form.superclass.constructor.call(this, null, config);
28640     this.url = this.url || this.action;
28641     if(!this.root){
28642         this.root = new Roo.form.Layout(Roo.applyIf({
28643             id: Roo.id()
28644         }, config));
28645     }
28646     this.active = this.root;
28647     /**
28648      * Array of all the buttons that have been added to this form via {@link addButton}
28649      * @type Array
28650      */
28651     this.buttons = [];
28652     this.allItems = [];
28653     this.addEvents({
28654         /**
28655          * @event clientvalidation
28656          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
28657          * @param {Form} this
28658          * @param {Boolean} valid true if the form has passed client-side validation
28659          */
28660         clientvalidation: true,
28661         /**
28662          * @event rendered
28663          * Fires when the form is rendered
28664          * @param {Roo.form.Form} form
28665          */
28666         rendered : true
28667     });
28668     
28669     if (this.progressUrl) {
28670             // push a hidden field onto the list of fields..
28671             this.addxtype( {
28672                     xns: Roo.form, 
28673                     xtype : 'Hidden', 
28674                     name : 'UPLOAD_IDENTIFIER' 
28675             });
28676         }
28677         
28678     
28679     Roo.each(xitems, this.addxtype, this);
28680     
28681     
28682     
28683 };
28684
28685 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
28686     /**
28687      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
28688      */
28689     /**
28690      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
28691      */
28692     /**
28693      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
28694      */
28695     buttonAlign:'center',
28696
28697     /**
28698      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
28699      */
28700     minButtonWidth:75,
28701
28702     /**
28703      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
28704      * This property cascades to child containers if not set.
28705      */
28706     labelAlign:'left',
28707
28708     /**
28709      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
28710      * fires a looping event with that state. This is required to bind buttons to the valid
28711      * state using the config value formBind:true on the button.
28712      */
28713     monitorValid : false,
28714
28715     /**
28716      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
28717      */
28718     monitorPoll : 200,
28719     
28720     /**
28721      * @cfg {String} progressUrl - Url to return progress data 
28722      */
28723     
28724     progressUrl : false,
28725   
28726     /**
28727      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
28728      * fields are added and the column is closed. If no fields are passed the column remains open
28729      * until end() is called.
28730      * @param {Object} config The config to pass to the column
28731      * @param {Field} field1 (optional)
28732      * @param {Field} field2 (optional)
28733      * @param {Field} etc (optional)
28734      * @return Column The column container object
28735      */
28736     column : function(c){
28737         var col = new Roo.form.Column(c);
28738         this.start(col);
28739         if(arguments.length > 1){ // duplicate code required because of Opera
28740             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28741             this.end();
28742         }
28743         return col;
28744     },
28745
28746     /**
28747      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
28748      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
28749      * until end() is called.
28750      * @param {Object} config The config to pass to the fieldset
28751      * @param {Field} field1 (optional)
28752      * @param {Field} field2 (optional)
28753      * @param {Field} etc (optional)
28754      * @return FieldSet The fieldset container object
28755      */
28756     fieldset : function(c){
28757         var fs = new Roo.form.FieldSet(c);
28758         this.start(fs);
28759         if(arguments.length > 1){ // duplicate code required because of Opera
28760             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28761             this.end();
28762         }
28763         return fs;
28764     },
28765
28766     /**
28767      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
28768      * fields are added and the container is closed. If no fields are passed the container remains open
28769      * until end() is called.
28770      * @param {Object} config The config to pass to the Layout
28771      * @param {Field} field1 (optional)
28772      * @param {Field} field2 (optional)
28773      * @param {Field} etc (optional)
28774      * @return Layout The container object
28775      */
28776     container : function(c){
28777         var l = new Roo.form.Layout(c);
28778         this.start(l);
28779         if(arguments.length > 1){ // duplicate code required because of Opera
28780             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28781             this.end();
28782         }
28783         return l;
28784     },
28785
28786     /**
28787      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
28788      * @param {Object} container A Roo.form.Layout or subclass of Layout
28789      * @return {Form} this
28790      */
28791     start : function(c){
28792         // cascade label info
28793         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
28794         this.active.stack.push(c);
28795         c.ownerCt = this.active;
28796         this.active = c;
28797         return this;
28798     },
28799
28800     /**
28801      * Closes the current open container
28802      * @return {Form} this
28803      */
28804     end : function(){
28805         if(this.active == this.root){
28806             return this;
28807         }
28808         this.active = this.active.ownerCt;
28809         return this;
28810     },
28811
28812     /**
28813      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
28814      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
28815      * as the label of the field.
28816      * @param {Field} field1
28817      * @param {Field} field2 (optional)
28818      * @param {Field} etc. (optional)
28819      * @return {Form} this
28820      */
28821     add : function(){
28822         this.active.stack.push.apply(this.active.stack, arguments);
28823         this.allItems.push.apply(this.allItems,arguments);
28824         var r = [];
28825         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
28826             if(a[i].isFormField){
28827                 r.push(a[i]);
28828             }
28829         }
28830         if(r.length > 0){
28831             Roo.form.Form.superclass.add.apply(this, r);
28832         }
28833         return this;
28834     },
28835     
28836
28837     
28838     
28839     
28840      /**
28841      * Find any element that has been added to a form, using it's ID or name
28842      * This can include framesets, columns etc. along with regular fields..
28843      * @param {String} id - id or name to find.
28844      
28845      * @return {Element} e - or false if nothing found.
28846      */
28847     findbyId : function(id)
28848     {
28849         var ret = false;
28850         if (!id) {
28851             return ret;
28852         }
28853         Roo.each(this.allItems, function(f){
28854             if (f.id == id || f.name == id ){
28855                 ret = f;
28856                 return false;
28857             }
28858         });
28859         return ret;
28860     },
28861
28862     
28863     
28864     /**
28865      * Render this form into the passed container. This should only be called once!
28866      * @param {String/HTMLElement/Element} container The element this component should be rendered into
28867      * @return {Form} this
28868      */
28869     render : function(ct)
28870     {
28871         
28872         
28873         
28874         ct = Roo.get(ct);
28875         var o = this.autoCreate || {
28876             tag: 'form',
28877             method : this.method || 'POST',
28878             id : this.id || Roo.id()
28879         };
28880         this.initEl(ct.createChild(o));
28881
28882         this.root.render(this.el);
28883         
28884        
28885              
28886         this.items.each(function(f){
28887             f.render('x-form-el-'+f.id);
28888         });
28889
28890         if(this.buttons.length > 0){
28891             // tables are required to maintain order and for correct IE layout
28892             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
28893                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
28894                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
28895             }}, null, true);
28896             var tr = tb.getElementsByTagName('tr')[0];
28897             for(var i = 0, len = this.buttons.length; i < len; i++) {
28898                 var b = this.buttons[i];
28899                 var td = document.createElement('td');
28900                 td.className = 'x-form-btn-td';
28901                 b.render(tr.appendChild(td));
28902             }
28903         }
28904         if(this.monitorValid){ // initialize after render
28905             this.startMonitoring();
28906         }
28907         this.fireEvent('rendered', this);
28908         return this;
28909     },
28910
28911     /**
28912      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
28913      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
28914      * object or a valid Roo.DomHelper element config
28915      * @param {Function} handler The function called when the button is clicked
28916      * @param {Object} scope (optional) The scope of the handler function
28917      * @return {Roo.Button}
28918      */
28919     addButton : function(config, handler, scope){
28920         var bc = {
28921             handler: handler,
28922             scope: scope,
28923             minWidth: this.minButtonWidth,
28924             hideParent:true
28925         };
28926         if(typeof config == "string"){
28927             bc.text = config;
28928         }else{
28929             Roo.apply(bc, config);
28930         }
28931         var btn = new Roo.Button(null, bc);
28932         this.buttons.push(btn);
28933         return btn;
28934     },
28935
28936      /**
28937      * Adds a series of form elements (using the xtype property as the factory method.
28938      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
28939      * @param {Object} config 
28940      */
28941     
28942     addxtype : function()
28943     {
28944         var ar = Array.prototype.slice.call(arguments, 0);
28945         var ret = false;
28946         for(var i = 0; i < ar.length; i++) {
28947             if (!ar[i]) {
28948                 continue; // skip -- if this happends something invalid got sent, we 
28949                 // should ignore it, as basically that interface element will not show up
28950                 // and that should be pretty obvious!!
28951             }
28952             
28953             if (Roo.form[ar[i].xtype]) {
28954                 ar[i].form = this;
28955                 var fe = Roo.factory(ar[i], Roo.form);
28956                 if (!ret) {
28957                     ret = fe;
28958                 }
28959                 fe.form = this;
28960                 if (fe.store) {
28961                     fe.store.form = this;
28962                 }
28963                 if (fe.isLayout) {  
28964                          
28965                     this.start(fe);
28966                     this.allItems.push(fe);
28967                     if (fe.items && fe.addxtype) {
28968                         fe.addxtype.apply(fe, fe.items);
28969                         delete fe.items;
28970                     }
28971                      this.end();
28972                     continue;
28973                 }
28974                 
28975                 
28976                  
28977                 this.add(fe);
28978               //  console.log('adding ' + ar[i].xtype);
28979             }
28980             if (ar[i].xtype == 'Button') {  
28981                 //console.log('adding button');
28982                 //console.log(ar[i]);
28983                 this.addButton(ar[i]);
28984                 this.allItems.push(fe);
28985                 continue;
28986             }
28987             
28988             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
28989                 alert('end is not supported on xtype any more, use items');
28990             //    this.end();
28991             //    //console.log('adding end');
28992             }
28993             
28994         }
28995         return ret;
28996     },
28997     
28998     /**
28999      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
29000      * option "monitorValid"
29001      */
29002     startMonitoring : function(){
29003         if(!this.bound){
29004             this.bound = true;
29005             Roo.TaskMgr.start({
29006                 run : this.bindHandler,
29007                 interval : this.monitorPoll || 200,
29008                 scope: this
29009             });
29010         }
29011     },
29012
29013     /**
29014      * Stops monitoring of the valid state of this form
29015      */
29016     stopMonitoring : function(){
29017         this.bound = false;
29018     },
29019
29020     // private
29021     bindHandler : function(){
29022         if(!this.bound){
29023             return false; // stops binding
29024         }
29025         var valid = true;
29026         this.items.each(function(f){
29027             if(!f.isValid(true)){
29028                 valid = false;
29029                 return false;
29030             }
29031         });
29032         for(var i = 0, len = this.buttons.length; i < len; i++){
29033             var btn = this.buttons[i];
29034             if(btn.formBind === true && btn.disabled === valid){
29035                 btn.setDisabled(!valid);
29036             }
29037         }
29038         this.fireEvent('clientvalidation', this, valid);
29039     }
29040     
29041     
29042     
29043     
29044     
29045     
29046     
29047     
29048 });
29049
29050
29051 // back compat
29052 Roo.Form = Roo.form.Form;
29053 /*
29054  * Based on:
29055  * Ext JS Library 1.1.1
29056  * Copyright(c) 2006-2007, Ext JS, LLC.
29057  *
29058  * Originally Released Under LGPL - original licence link has changed is not relivant.
29059  *
29060  * Fork - LGPL
29061  * <script type="text/javascript">
29062  */
29063  
29064  /**
29065  * @class Roo.form.Action
29066  * Internal Class used to handle form actions
29067  * @constructor
29068  * @param {Roo.form.BasicForm} el The form element or its id
29069  * @param {Object} config Configuration options
29070  */
29071  
29072  
29073 // define the action interface
29074 Roo.form.Action = function(form, options){
29075     this.form = form;
29076     this.options = options || {};
29077 };
29078 /**
29079  * Client Validation Failed
29080  * @const 
29081  */
29082 Roo.form.Action.CLIENT_INVALID = 'client';
29083 /**
29084  * Server Validation Failed
29085  * @const 
29086  */
29087  Roo.form.Action.SERVER_INVALID = 'server';
29088  /**
29089  * Connect to Server Failed
29090  * @const 
29091  */
29092 Roo.form.Action.CONNECT_FAILURE = 'connect';
29093 /**
29094  * Reading Data from Server Failed
29095  * @const 
29096  */
29097 Roo.form.Action.LOAD_FAILURE = 'load';
29098
29099 Roo.form.Action.prototype = {
29100     type : 'default',
29101     failureType : undefined,
29102     response : undefined,
29103     result : undefined,
29104
29105     // interface method
29106     run : function(options){
29107
29108     },
29109
29110     // interface method
29111     success : function(response){
29112
29113     },
29114
29115     // interface method
29116     handleResponse : function(response){
29117
29118     },
29119
29120     // default connection failure
29121     failure : function(response){
29122         
29123         this.response = response;
29124         this.failureType = Roo.form.Action.CONNECT_FAILURE;
29125         this.form.afterAction(this, false);
29126     },
29127
29128     processResponse : function(response){
29129         this.response = response;
29130         if(!response.responseText){
29131             return true;
29132         }
29133         this.result = this.handleResponse(response);
29134         return this.result;
29135     },
29136
29137     // utility functions used internally
29138     getUrl : function(appendParams){
29139         var url = this.options.url || this.form.url || this.form.el.dom.action;
29140         if(appendParams){
29141             var p = this.getParams();
29142             if(p){
29143                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
29144             }
29145         }
29146         return url;
29147     },
29148
29149     getMethod : function(){
29150         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
29151     },
29152
29153     getParams : function(){
29154         var bp = this.form.baseParams;
29155         var p = this.options.params;
29156         if(p){
29157             if(typeof p == "object"){
29158                 p = Roo.urlEncode(Roo.applyIf(p, bp));
29159             }else if(typeof p == 'string' && bp){
29160                 p += '&' + Roo.urlEncode(bp);
29161             }
29162         }else if(bp){
29163             p = Roo.urlEncode(bp);
29164         }
29165         return p;
29166     },
29167
29168     createCallback : function(){
29169         return {
29170             success: this.success,
29171             failure: this.failure,
29172             scope: this,
29173             timeout: (this.form.timeout*1000),
29174             upload: this.form.fileUpload ? this.success : undefined
29175         };
29176     }
29177 };
29178
29179 Roo.form.Action.Submit = function(form, options){
29180     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
29181 };
29182
29183 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
29184     type : 'submit',
29185
29186     haveProgress : false,
29187     uploadComplete : false,
29188     
29189     // uploadProgress indicator.
29190     uploadProgress : function()
29191     {
29192         if (!this.form.progressUrl) {
29193             return;
29194         }
29195         
29196         if (!this.haveProgress) {
29197             Roo.MessageBox.progress("Uploading", "Uploading");
29198         }
29199         if (this.uploadComplete) {
29200            Roo.MessageBox.hide();
29201            return;
29202         }
29203         
29204         this.haveProgress = true;
29205    
29206         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
29207         
29208         var c = new Roo.data.Connection();
29209         c.request({
29210             url : this.form.progressUrl,
29211             params: {
29212                 id : uid
29213             },
29214             method: 'GET',
29215             success : function(req){
29216                //console.log(data);
29217                 var rdata = false;
29218                 var edata;
29219                 try  {
29220                    rdata = Roo.decode(req.responseText)
29221                 } catch (e) {
29222                     Roo.log("Invalid data from server..");
29223                     Roo.log(edata);
29224                     return;
29225                 }
29226                 if (!rdata || !rdata.success) {
29227                     Roo.log(rdata);
29228                     Roo.MessageBox.alert(Roo.encode(rdata));
29229                     return;
29230                 }
29231                 var data = rdata.data;
29232                 
29233                 if (this.uploadComplete) {
29234                    Roo.MessageBox.hide();
29235                    return;
29236                 }
29237                    
29238                 if (data){
29239                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
29240                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
29241                     );
29242                 }
29243                 this.uploadProgress.defer(2000,this);
29244             },
29245        
29246             failure: function(data) {
29247                 Roo.log('progress url failed ');
29248                 Roo.log(data);
29249             },
29250             scope : this
29251         });
29252            
29253     },
29254     
29255     
29256     run : function()
29257     {
29258         // run get Values on the form, so it syncs any secondary forms.
29259         this.form.getValues();
29260         
29261         var o = this.options;
29262         var method = this.getMethod();
29263         var isPost = method == 'POST';
29264         if(o.clientValidation === false || this.form.isValid()){
29265             
29266             if (this.form.progressUrl) {
29267                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
29268                     (new Date() * 1) + '' + Math.random());
29269                     
29270             } 
29271             
29272             
29273             Roo.Ajax.request(Roo.apply(this.createCallback(), {
29274                 form:this.form.el.dom,
29275                 url:this.getUrl(!isPost),
29276                 method: method,
29277                 params:isPost ? this.getParams() : null,
29278                 isUpload: this.form.fileUpload
29279             }));
29280             
29281             this.uploadProgress();
29282
29283         }else if (o.clientValidation !== false){ // client validation failed
29284             this.failureType = Roo.form.Action.CLIENT_INVALID;
29285             this.form.afterAction(this, false);
29286         }
29287     },
29288
29289     success : function(response)
29290     {
29291         this.uploadComplete= true;
29292         if (this.haveProgress) {
29293             Roo.MessageBox.hide();
29294         }
29295         
29296         
29297         var result = this.processResponse(response);
29298         if(result === true || result.success){
29299             this.form.afterAction(this, true);
29300             return;
29301         }
29302         if(result.errors){
29303             this.form.markInvalid(result.errors);
29304             this.failureType = Roo.form.Action.SERVER_INVALID;
29305         }
29306         this.form.afterAction(this, false);
29307     },
29308     failure : function(response)
29309     {
29310         this.uploadComplete= true;
29311         if (this.haveProgress) {
29312             Roo.MessageBox.hide();
29313         }
29314         
29315         this.response = response;
29316         this.failureType = Roo.form.Action.CONNECT_FAILURE;
29317         this.form.afterAction(this, false);
29318     },
29319     
29320     handleResponse : function(response){
29321         if(this.form.errorReader){
29322             var rs = this.form.errorReader.read(response);
29323             var errors = [];
29324             if(rs.records){
29325                 for(var i = 0, len = rs.records.length; i < len; i++) {
29326                     var r = rs.records[i];
29327                     errors[i] = r.data;
29328                 }
29329             }
29330             if(errors.length < 1){
29331                 errors = null;
29332             }
29333             return {
29334                 success : rs.success,
29335                 errors : errors
29336             };
29337         }
29338         var ret = false;
29339         try {
29340             ret = Roo.decode(response.responseText);
29341         } catch (e) {
29342             ret = {
29343                 success: false,
29344                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
29345                 errors : []
29346             };
29347         }
29348         return ret;
29349         
29350     }
29351 });
29352
29353
29354 Roo.form.Action.Load = function(form, options){
29355     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
29356     this.reader = this.form.reader;
29357 };
29358
29359 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
29360     type : 'load',
29361
29362     run : function(){
29363         
29364         Roo.Ajax.request(Roo.apply(
29365                 this.createCallback(), {
29366                     method:this.getMethod(),
29367                     url:this.getUrl(false),
29368                     params:this.getParams()
29369         }));
29370     },
29371
29372     success : function(response){
29373         
29374         var result = this.processResponse(response);
29375         if(result === true || !result.success || !result.data){
29376             this.failureType = Roo.form.Action.LOAD_FAILURE;
29377             this.form.afterAction(this, false);
29378             return;
29379         }
29380         this.form.clearInvalid();
29381         this.form.setValues(result.data);
29382         this.form.afterAction(this, true);
29383     },
29384
29385     handleResponse : function(response){
29386         if(this.form.reader){
29387             var rs = this.form.reader.read(response);
29388             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
29389             return {
29390                 success : rs.success,
29391                 data : data
29392             };
29393         }
29394         return Roo.decode(response.responseText);
29395     }
29396 });
29397
29398 Roo.form.Action.ACTION_TYPES = {
29399     'load' : Roo.form.Action.Load,
29400     'submit' : Roo.form.Action.Submit
29401 };/*
29402  * Based on:
29403  * Ext JS Library 1.1.1
29404  * Copyright(c) 2006-2007, Ext JS, LLC.
29405  *
29406  * Originally Released Under LGPL - original licence link has changed is not relivant.
29407  *
29408  * Fork - LGPL
29409  * <script type="text/javascript">
29410  */
29411  
29412 /**
29413  * @class Roo.form.Layout
29414  * @extends Roo.Component
29415  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
29416  * @constructor
29417  * @param {Object} config Configuration options
29418  */
29419 Roo.form.Layout = function(config){
29420     var xitems = [];
29421     if (config.items) {
29422         xitems = config.items;
29423         delete config.items;
29424     }
29425     Roo.form.Layout.superclass.constructor.call(this, config);
29426     this.stack = [];
29427     Roo.each(xitems, this.addxtype, this);
29428      
29429 };
29430
29431 Roo.extend(Roo.form.Layout, Roo.Component, {
29432     /**
29433      * @cfg {String/Object} autoCreate
29434      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
29435      */
29436     /**
29437      * @cfg {String/Object/Function} style
29438      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
29439      * a function which returns such a specification.
29440      */
29441     /**
29442      * @cfg {String} labelAlign
29443      * Valid values are "left," "top" and "right" (defaults to "left")
29444      */
29445     /**
29446      * @cfg {Number} labelWidth
29447      * Fixed width in pixels of all field labels (defaults to undefined)
29448      */
29449     /**
29450      * @cfg {Boolean} clear
29451      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
29452      */
29453     clear : true,
29454     /**
29455      * @cfg {String} labelSeparator
29456      * The separator to use after field labels (defaults to ':')
29457      */
29458     labelSeparator : ':',
29459     /**
29460      * @cfg {Boolean} hideLabels
29461      * True to suppress the display of field labels in this layout (defaults to false)
29462      */
29463     hideLabels : false,
29464
29465     // private
29466     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
29467     
29468     isLayout : true,
29469     
29470     // private
29471     onRender : function(ct, position){
29472         if(this.el){ // from markup
29473             this.el = Roo.get(this.el);
29474         }else {  // generate
29475             var cfg = this.getAutoCreate();
29476             this.el = ct.createChild(cfg, position);
29477         }
29478         if(this.style){
29479             this.el.applyStyles(this.style);
29480         }
29481         if(this.labelAlign){
29482             this.el.addClass('x-form-label-'+this.labelAlign);
29483         }
29484         if(this.hideLabels){
29485             this.labelStyle = "display:none";
29486             this.elementStyle = "padding-left:0;";
29487         }else{
29488             if(typeof this.labelWidth == 'number'){
29489                 this.labelStyle = "width:"+this.labelWidth+"px;";
29490                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
29491             }
29492             if(this.labelAlign == 'top'){
29493                 this.labelStyle = "width:auto;";
29494                 this.elementStyle = "padding-left:0;";
29495             }
29496         }
29497         var stack = this.stack;
29498         var slen = stack.length;
29499         if(slen > 0){
29500             if(!this.fieldTpl){
29501                 var t = new Roo.Template(
29502                     '<div class="x-form-item {5}">',
29503                         '<label for="{0}" style="{2}">{1}{4}</label>',
29504                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
29505                         '</div>',
29506                     '</div><div class="x-form-clear-left"></div>'
29507                 );
29508                 t.disableFormats = true;
29509                 t.compile();
29510                 Roo.form.Layout.prototype.fieldTpl = t;
29511             }
29512             for(var i = 0; i < slen; i++) {
29513                 if(stack[i].isFormField){
29514                     this.renderField(stack[i]);
29515                 }else{
29516                     this.renderComponent(stack[i]);
29517                 }
29518             }
29519         }
29520         if(this.clear){
29521             this.el.createChild({cls:'x-form-clear'});
29522         }
29523     },
29524
29525     // private
29526     renderField : function(f){
29527         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
29528                f.id, //0
29529                f.fieldLabel, //1
29530                f.labelStyle||this.labelStyle||'', //2
29531                this.elementStyle||'', //3
29532                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
29533                f.itemCls||this.itemCls||''  //5
29534        ], true).getPrevSibling());
29535     },
29536
29537     // private
29538     renderComponent : function(c){
29539         c.render(c.isLayout ? this.el : this.el.createChild());    
29540     },
29541     /**
29542      * Adds a object form elements (using the xtype property as the factory method.)
29543      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
29544      * @param {Object} config 
29545      */
29546     addxtype : function(o)
29547     {
29548         // create the lement.
29549         o.form = this.form;
29550         var fe = Roo.factory(o, Roo.form);
29551         this.form.allItems.push(fe);
29552         this.stack.push(fe);
29553         
29554         if (fe.isFormField) {
29555             this.form.items.add(fe);
29556         }
29557          
29558         return fe;
29559     }
29560 });
29561
29562 /**
29563  * @class Roo.form.Column
29564  * @extends Roo.form.Layout
29565  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
29566  * @constructor
29567  * @param {Object} config Configuration options
29568  */
29569 Roo.form.Column = function(config){
29570     Roo.form.Column.superclass.constructor.call(this, config);
29571 };
29572
29573 Roo.extend(Roo.form.Column, Roo.form.Layout, {
29574     /**
29575      * @cfg {Number/String} width
29576      * The fixed width of the column in pixels or CSS value (defaults to "auto")
29577      */
29578     /**
29579      * @cfg {String/Object} autoCreate
29580      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
29581      */
29582
29583     // private
29584     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
29585
29586     // private
29587     onRender : function(ct, position){
29588         Roo.form.Column.superclass.onRender.call(this, ct, position);
29589         if(this.width){
29590             this.el.setWidth(this.width);
29591         }
29592     }
29593 });
29594
29595
29596 /**
29597  * @class Roo.form.Row
29598  * @extends Roo.form.Layout
29599  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
29600  * @constructor
29601  * @param {Object} config Configuration options
29602  */
29603
29604  
29605 Roo.form.Row = function(config){
29606     Roo.form.Row.superclass.constructor.call(this, config);
29607 };
29608  
29609 Roo.extend(Roo.form.Row, Roo.form.Layout, {
29610       /**
29611      * @cfg {Number/String} width
29612      * The fixed width of the column in pixels or CSS value (defaults to "auto")
29613      */
29614     /**
29615      * @cfg {Number/String} height
29616      * The fixed height of the column in pixels or CSS value (defaults to "auto")
29617      */
29618     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
29619     
29620     padWidth : 20,
29621     // private
29622     onRender : function(ct, position){
29623         //console.log('row render');
29624         if(!this.rowTpl){
29625             var t = new Roo.Template(
29626                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
29627                     '<label for="{0}" style="{2}">{1}{4}</label>',
29628                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
29629                     '</div>',
29630                 '</div>'
29631             );
29632             t.disableFormats = true;
29633             t.compile();
29634             Roo.form.Layout.prototype.rowTpl = t;
29635         }
29636         this.fieldTpl = this.rowTpl;
29637         
29638         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
29639         var labelWidth = 100;
29640         
29641         if ((this.labelAlign != 'top')) {
29642             if (typeof this.labelWidth == 'number') {
29643                 labelWidth = this.labelWidth
29644             }
29645             this.padWidth =  20 + labelWidth;
29646             
29647         }
29648         
29649         Roo.form.Column.superclass.onRender.call(this, ct, position);
29650         if(this.width){
29651             this.el.setWidth(this.width);
29652         }
29653         if(this.height){
29654             this.el.setHeight(this.height);
29655         }
29656     },
29657     
29658     // private
29659     renderField : function(f){
29660         f.fieldEl = this.fieldTpl.append(this.el, [
29661                f.id, f.fieldLabel,
29662                f.labelStyle||this.labelStyle||'',
29663                this.elementStyle||'',
29664                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
29665                f.itemCls||this.itemCls||'',
29666                f.width ? f.width + this.padWidth : 160 + this.padWidth
29667        ],true);
29668     }
29669 });
29670  
29671
29672 /**
29673  * @class Roo.form.FieldSet
29674  * @extends Roo.form.Layout
29675  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
29676  * @constructor
29677  * @param {Object} config Configuration options
29678  */
29679 Roo.form.FieldSet = function(config){
29680     Roo.form.FieldSet.superclass.constructor.call(this, config);
29681 };
29682
29683 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
29684     /**
29685      * @cfg {String} legend
29686      * The text to display as the legend for the FieldSet (defaults to '')
29687      */
29688     /**
29689      * @cfg {String/Object} autoCreate
29690      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
29691      */
29692
29693     // private
29694     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
29695
29696     // private
29697     onRender : function(ct, position){
29698         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
29699         if(this.legend){
29700             this.setLegend(this.legend);
29701         }
29702     },
29703
29704     // private
29705     setLegend : function(text){
29706         if(this.rendered){
29707             this.el.child('legend').update(text);
29708         }
29709     }
29710 });/*
29711  * Based on:
29712  * Ext JS Library 1.1.1
29713  * Copyright(c) 2006-2007, Ext JS, LLC.
29714  *
29715  * Originally Released Under LGPL - original licence link has changed is not relivant.
29716  *
29717  * Fork - LGPL
29718  * <script type="text/javascript">
29719  */
29720 /**
29721  * @class Roo.form.VTypes
29722  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
29723  * @singleton
29724  */
29725 Roo.form.VTypes = function(){
29726     // closure these in so they are only created once.
29727     var alpha = /^[a-zA-Z_]+$/;
29728     var alphanum = /^[a-zA-Z0-9_]+$/;
29729     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
29730     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
29731
29732     // All these messages and functions are configurable
29733     return {
29734         /**
29735          * The function used to validate email addresses
29736          * @param {String} value The email address
29737          */
29738         'email' : function(v){
29739             return email.test(v);
29740         },
29741         /**
29742          * The error text to display when the email validation function returns false
29743          * @type String
29744          */
29745         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
29746         /**
29747          * The keystroke filter mask to be applied on email input
29748          * @type RegExp
29749          */
29750         'emailMask' : /[a-z0-9_\.\-@]/i,
29751
29752         /**
29753          * The function used to validate URLs
29754          * @param {String} value The URL
29755          */
29756         'url' : function(v){
29757             return url.test(v);
29758         },
29759         /**
29760          * The error text to display when the url validation function returns false
29761          * @type String
29762          */
29763         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
29764         
29765         /**
29766          * The function used to validate alpha values
29767          * @param {String} value The value
29768          */
29769         'alpha' : function(v){
29770             return alpha.test(v);
29771         },
29772         /**
29773          * The error text to display when the alpha validation function returns false
29774          * @type String
29775          */
29776         'alphaText' : 'This field should only contain letters and _',
29777         /**
29778          * The keystroke filter mask to be applied on alpha input
29779          * @type RegExp
29780          */
29781         'alphaMask' : /[a-z_]/i,
29782
29783         /**
29784          * The function used to validate alphanumeric values
29785          * @param {String} value The value
29786          */
29787         'alphanum' : function(v){
29788             return alphanum.test(v);
29789         },
29790         /**
29791          * The error text to display when the alphanumeric validation function returns false
29792          * @type String
29793          */
29794         'alphanumText' : 'This field should only contain letters, numbers and _',
29795         /**
29796          * The keystroke filter mask to be applied on alphanumeric input
29797          * @type RegExp
29798          */
29799         'alphanumMask' : /[a-z0-9_]/i
29800     };
29801 }();//<script type="text/javascript">
29802
29803 /**
29804  * @class Roo.form.FCKeditor
29805  * @extends Roo.form.TextArea
29806  * Wrapper around the FCKEditor http://www.fckeditor.net
29807  * @constructor
29808  * Creates a new FCKeditor
29809  * @param {Object} config Configuration options
29810  */
29811 Roo.form.FCKeditor = function(config){
29812     Roo.form.FCKeditor.superclass.constructor.call(this, config);
29813     this.addEvents({
29814          /**
29815          * @event editorinit
29816          * Fired when the editor is initialized - you can add extra handlers here..
29817          * @param {FCKeditor} this
29818          * @param {Object} the FCK object.
29819          */
29820         editorinit : true
29821     });
29822     
29823     
29824 };
29825 Roo.form.FCKeditor.editors = { };
29826 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
29827 {
29828     //defaultAutoCreate : {
29829     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
29830     //},
29831     // private
29832     /**
29833      * @cfg {Object} fck options - see fck manual for details.
29834      */
29835     fckconfig : false,
29836     
29837     /**
29838      * @cfg {Object} fck toolbar set (Basic or Default)
29839      */
29840     toolbarSet : 'Basic',
29841     /**
29842      * @cfg {Object} fck BasePath
29843      */ 
29844     basePath : '/fckeditor/',
29845     
29846     
29847     frame : false,
29848     
29849     value : '',
29850     
29851    
29852     onRender : function(ct, position)
29853     {
29854         if(!this.el){
29855             this.defaultAutoCreate = {
29856                 tag: "textarea",
29857                 style:"width:300px;height:60px;",
29858                 autocomplete: "off"
29859             };
29860         }
29861         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
29862         /*
29863         if(this.grow){
29864             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
29865             if(this.preventScrollbars){
29866                 this.el.setStyle("overflow", "hidden");
29867             }
29868             this.el.setHeight(this.growMin);
29869         }
29870         */
29871         //console.log('onrender' + this.getId() );
29872         Roo.form.FCKeditor.editors[this.getId()] = this;
29873          
29874
29875         this.replaceTextarea() ;
29876         
29877     },
29878     
29879     getEditor : function() {
29880         return this.fckEditor;
29881     },
29882     /**
29883      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
29884      * @param {Mixed} value The value to set
29885      */
29886     
29887     
29888     setValue : function(value)
29889     {
29890         //console.log('setValue: ' + value);
29891         
29892         if(typeof(value) == 'undefined') { // not sure why this is happending...
29893             return;
29894         }
29895         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
29896         
29897         //if(!this.el || !this.getEditor()) {
29898         //    this.value = value;
29899             //this.setValue.defer(100,this,[value]);    
29900         //    return;
29901         //} 
29902         
29903         if(!this.getEditor()) {
29904             return;
29905         }
29906         
29907         this.getEditor().SetData(value);
29908         
29909         //
29910
29911     },
29912
29913     /**
29914      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
29915      * @return {Mixed} value The field value
29916      */
29917     getValue : function()
29918     {
29919         
29920         if (this.frame && this.frame.dom.style.display == 'none') {
29921             return Roo.form.FCKeditor.superclass.getValue.call(this);
29922         }
29923         
29924         if(!this.el || !this.getEditor()) {
29925            
29926            // this.getValue.defer(100,this); 
29927             return this.value;
29928         }
29929        
29930         
29931         var value=this.getEditor().GetData();
29932         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
29933         return Roo.form.FCKeditor.superclass.getValue.call(this);
29934         
29935
29936     },
29937
29938     /**
29939      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
29940      * @return {Mixed} value The field value
29941      */
29942     getRawValue : function()
29943     {
29944         if (this.frame && this.frame.dom.style.display == 'none') {
29945             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
29946         }
29947         
29948         if(!this.el || !this.getEditor()) {
29949             //this.getRawValue.defer(100,this); 
29950             return this.value;
29951             return;
29952         }
29953         
29954         
29955         
29956         var value=this.getEditor().GetData();
29957         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
29958         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
29959          
29960     },
29961     
29962     setSize : function(w,h) {
29963         
29964         
29965         
29966         //if (this.frame && this.frame.dom.style.display == 'none') {
29967         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
29968         //    return;
29969         //}
29970         //if(!this.el || !this.getEditor()) {
29971         //    this.setSize.defer(100,this, [w,h]); 
29972         //    return;
29973         //}
29974         
29975         
29976         
29977         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
29978         
29979         this.frame.dom.setAttribute('width', w);
29980         this.frame.dom.setAttribute('height', h);
29981         this.frame.setSize(w,h);
29982         
29983     },
29984     
29985     toggleSourceEdit : function(value) {
29986         
29987       
29988          
29989         this.el.dom.style.display = value ? '' : 'none';
29990         this.frame.dom.style.display = value ?  'none' : '';
29991         
29992     },
29993     
29994     
29995     focus: function(tag)
29996     {
29997         if (this.frame.dom.style.display == 'none') {
29998             return Roo.form.FCKeditor.superclass.focus.call(this);
29999         }
30000         if(!this.el || !this.getEditor()) {
30001             this.focus.defer(100,this, [tag]); 
30002             return;
30003         }
30004         
30005         
30006         
30007         
30008         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
30009         this.getEditor().Focus();
30010         if (tgs.length) {
30011             if (!this.getEditor().Selection.GetSelection()) {
30012                 this.focus.defer(100,this, [tag]); 
30013                 return;
30014             }
30015             
30016             
30017             var r = this.getEditor().EditorDocument.createRange();
30018             r.setStart(tgs[0],0);
30019             r.setEnd(tgs[0],0);
30020             this.getEditor().Selection.GetSelection().removeAllRanges();
30021             this.getEditor().Selection.GetSelection().addRange(r);
30022             this.getEditor().Focus();
30023         }
30024         
30025     },
30026     
30027     
30028     
30029     replaceTextarea : function()
30030     {
30031         if ( document.getElementById( this.getId() + '___Frame' ) )
30032             return ;
30033         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
30034         //{
30035             // We must check the elements firstly using the Id and then the name.
30036         var oTextarea = document.getElementById( this.getId() );
30037         
30038         var colElementsByName = document.getElementsByName( this.getId() ) ;
30039          
30040         oTextarea.style.display = 'none' ;
30041
30042         if ( oTextarea.tabIndex ) {            
30043             this.TabIndex = oTextarea.tabIndex ;
30044         }
30045         
30046         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
30047         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
30048         this.frame = Roo.get(this.getId() + '___Frame')
30049     },
30050     
30051     _getConfigHtml : function()
30052     {
30053         var sConfig = '' ;
30054
30055         for ( var o in this.fckconfig ) {
30056             sConfig += sConfig.length > 0  ? '&amp;' : '';
30057             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
30058         }
30059
30060         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
30061     },
30062     
30063     
30064     _getIFrameHtml : function()
30065     {
30066         var sFile = 'fckeditor.html' ;
30067         /* no idea what this is about..
30068         try
30069         {
30070             if ( (/fcksource=true/i).test( window.top.location.search ) )
30071                 sFile = 'fckeditor.original.html' ;
30072         }
30073         catch (e) { 
30074         */
30075
30076         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
30077         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
30078         
30079         
30080         var html = '<iframe id="' + this.getId() +
30081             '___Frame" src="' + sLink +
30082             '" width="' + this.width +
30083             '" height="' + this.height + '"' +
30084             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
30085             ' frameborder="0" scrolling="no"></iframe>' ;
30086
30087         return html ;
30088     },
30089     
30090     _insertHtmlBefore : function( html, element )
30091     {
30092         if ( element.insertAdjacentHTML )       {
30093             // IE
30094             element.insertAdjacentHTML( 'beforeBegin', html ) ;
30095         } else { // Gecko
30096             var oRange = document.createRange() ;
30097             oRange.setStartBefore( element ) ;
30098             var oFragment = oRange.createContextualFragment( html );
30099             element.parentNode.insertBefore( oFragment, element ) ;
30100         }
30101     }
30102     
30103     
30104   
30105     
30106     
30107     
30108     
30109
30110 });
30111
30112 //Roo.reg('fckeditor', Roo.form.FCKeditor);
30113
30114 function FCKeditor_OnComplete(editorInstance){
30115     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
30116     f.fckEditor = editorInstance;
30117     //console.log("loaded");
30118     f.fireEvent('editorinit', f, editorInstance);
30119
30120   
30121
30122  
30123
30124
30125
30126
30127
30128
30129
30130
30131
30132
30133
30134
30135
30136
30137
30138 //<script type="text/javascript">
30139 /**
30140  * @class Roo.form.GridField
30141  * @extends Roo.form.Field
30142  * Embed a grid (or editable grid into a form)
30143  * STATUS ALPHA
30144  * 
30145  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
30146  * it needs 
30147  * xgrid.store = Roo.data.Store
30148  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
30149  * xgrid.store.reader = Roo.data.JsonReader 
30150  * 
30151  * 
30152  * @constructor
30153  * Creates a new GridField
30154  * @param {Object} config Configuration options
30155  */
30156 Roo.form.GridField = function(config){
30157     Roo.form.GridField.superclass.constructor.call(this, config);
30158      
30159 };
30160
30161 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
30162     /**
30163      * @cfg {Number} width  - used to restrict width of grid..
30164      */
30165     width : 100,
30166     /**
30167      * @cfg {Number} height - used to restrict height of grid..
30168      */
30169     height : 50,
30170      /**
30171      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
30172          * 
30173          *}
30174      */
30175     xgrid : false, 
30176     /**
30177      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30178      * {tag: "input", type: "checkbox", autocomplete: "off"})
30179      */
30180    // defaultAutoCreate : { tag: 'div' },
30181     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
30182     /**
30183      * @cfg {String} addTitle Text to include for adding a title.
30184      */
30185     addTitle : false,
30186     //
30187     onResize : function(){
30188         Roo.form.Field.superclass.onResize.apply(this, arguments);
30189     },
30190
30191     initEvents : function(){
30192         // Roo.form.Checkbox.superclass.initEvents.call(this);
30193         // has no events...
30194        
30195     },
30196
30197
30198     getResizeEl : function(){
30199         return this.wrap;
30200     },
30201
30202     getPositionEl : function(){
30203         return this.wrap;
30204     },
30205
30206     // private
30207     onRender : function(ct, position){
30208         
30209         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
30210         var style = this.style;
30211         delete this.style;
30212         
30213         Roo.form.GridField.superclass.onRender.call(this, ct, position);
30214         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
30215         this.viewEl = this.wrap.createChild({ tag: 'div' });
30216         if (style) {
30217             this.viewEl.applyStyles(style);
30218         }
30219         if (this.width) {
30220             this.viewEl.setWidth(this.width);
30221         }
30222         if (this.height) {
30223             this.viewEl.setHeight(this.height);
30224         }
30225         //if(this.inputValue !== undefined){
30226         //this.setValue(this.value);
30227         
30228         
30229         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
30230         
30231         
30232         this.grid.render();
30233         this.grid.getDataSource().on('remove', this.refreshValue, this);
30234         this.grid.getDataSource().on('update', this.refreshValue, this);
30235         this.grid.on('afteredit', this.refreshValue, this);
30236  
30237     },
30238      
30239     
30240     /**
30241      * Sets the value of the item. 
30242      * @param {String} either an object  or a string..
30243      */
30244     setValue : function(v){
30245         //this.value = v;
30246         v = v || []; // empty set..
30247         // this does not seem smart - it really only affects memoryproxy grids..
30248         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
30249             var ds = this.grid.getDataSource();
30250             // assumes a json reader..
30251             var data = {}
30252             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
30253             ds.loadData( data);
30254         }
30255         // clear selection so it does not get stale.
30256         if (this.grid.sm) { 
30257             this.grid.sm.clearSelections();
30258         }
30259         
30260         Roo.form.GridField.superclass.setValue.call(this, v);
30261         this.refreshValue();
30262         // should load data in the grid really....
30263     },
30264     
30265     // private
30266     refreshValue: function() {
30267          var val = [];
30268         this.grid.getDataSource().each(function(r) {
30269             val.push(r.data);
30270         });
30271         this.el.dom.value = Roo.encode(val);
30272     }
30273     
30274      
30275     
30276     
30277 });/*
30278  * Based on:
30279  * Ext JS Library 1.1.1
30280  * Copyright(c) 2006-2007, Ext JS, LLC.
30281  *
30282  * Originally Released Under LGPL - original licence link has changed is not relivant.
30283  *
30284  * Fork - LGPL
30285  * <script type="text/javascript">
30286  */
30287 /**
30288  * @class Roo.form.DisplayField
30289  * @extends Roo.form.Field
30290  * A generic Field to display non-editable data.
30291  * @constructor
30292  * Creates a new Display Field item.
30293  * @param {Object} config Configuration options
30294  */
30295 Roo.form.DisplayField = function(config){
30296     Roo.form.DisplayField.superclass.constructor.call(this, config);
30297     
30298 };
30299
30300 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
30301     inputType:      'hidden',
30302     allowBlank:     true,
30303     readOnly:         true,
30304     
30305  
30306     /**
30307      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30308      */
30309     focusClass : undefined,
30310     /**
30311      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30312      */
30313     fieldClass: 'x-form-field',
30314     
30315      /**
30316      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
30317      */
30318     valueRenderer: undefined,
30319     
30320     width: 100,
30321     /**
30322      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30323      * {tag: "input", type: "checkbox", autocomplete: "off"})
30324      */
30325      
30326  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
30327
30328     onResize : function(){
30329         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
30330         
30331     },
30332
30333     initEvents : function(){
30334         // Roo.form.Checkbox.superclass.initEvents.call(this);
30335         // has no events...
30336        
30337     },
30338
30339
30340     getResizeEl : function(){
30341         return this.wrap;
30342     },
30343
30344     getPositionEl : function(){
30345         return this.wrap;
30346     },
30347
30348     // private
30349     onRender : function(ct, position){
30350         
30351         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
30352         //if(this.inputValue !== undefined){
30353         this.wrap = this.el.wrap();
30354         
30355         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
30356         
30357         if (this.bodyStyle) {
30358             this.viewEl.applyStyles(this.bodyStyle);
30359         }
30360         //this.viewEl.setStyle('padding', '2px');
30361         
30362         this.setValue(this.value);
30363         
30364     },
30365 /*
30366     // private
30367     initValue : Roo.emptyFn,
30368
30369   */
30370
30371         // private
30372     onClick : function(){
30373         
30374     },
30375
30376     /**
30377      * Sets the checked state of the checkbox.
30378      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
30379      */
30380     setValue : function(v){
30381         this.value = v;
30382         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
30383         // this might be called before we have a dom element..
30384         if (!this.viewEl) {
30385             return;
30386         }
30387         this.viewEl.dom.innerHTML = html;
30388         Roo.form.DisplayField.superclass.setValue.call(this, v);
30389
30390     }
30391 });/*
30392  * 
30393  * Licence- LGPL
30394  * 
30395  */
30396
30397 /**
30398  * @class Roo.form.DayPicker
30399  * @extends Roo.form.Field
30400  * A Day picker show [M] [T] [W] ....
30401  * @constructor
30402  * Creates a new Day Picker
30403  * @param {Object} config Configuration options
30404  */
30405 Roo.form.DayPicker= function(config){
30406     Roo.form.DayPicker.superclass.constructor.call(this, config);
30407      
30408 };
30409
30410 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
30411     /**
30412      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30413      */
30414     focusClass : undefined,
30415     /**
30416      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30417      */
30418     fieldClass: "x-form-field",
30419    
30420     /**
30421      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30422      * {tag: "input", type: "checkbox", autocomplete: "off"})
30423      */
30424     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
30425     
30426    
30427     actionMode : 'viewEl', 
30428     //
30429     // private
30430  
30431     inputType : 'hidden',
30432     
30433      
30434     inputElement: false, // real input element?
30435     basedOn: false, // ????
30436     
30437     isFormField: true, // not sure where this is needed!!!!
30438
30439     onResize : function(){
30440         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
30441         if(!this.boxLabel){
30442             this.el.alignTo(this.wrap, 'c-c');
30443         }
30444     },
30445
30446     initEvents : function(){
30447         Roo.form.Checkbox.superclass.initEvents.call(this);
30448         this.el.on("click", this.onClick,  this);
30449         this.el.on("change", this.onClick,  this);
30450     },
30451
30452
30453     getResizeEl : function(){
30454         return this.wrap;
30455     },
30456
30457     getPositionEl : function(){
30458         return this.wrap;
30459     },
30460
30461     
30462     // private
30463     onRender : function(ct, position){
30464         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
30465        
30466         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
30467         
30468         var r1 = '<table><tr>';
30469         var r2 = '<tr class="x-form-daypick-icons">';
30470         for (var i=0; i < 7; i++) {
30471             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
30472             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
30473         }
30474         
30475         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
30476         viewEl.select('img').on('click', this.onClick, this);
30477         this.viewEl = viewEl;   
30478         
30479         
30480         // this will not work on Chrome!!!
30481         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
30482         this.el.on('propertychange', this.setFromHidden,  this);  //ie
30483         
30484         
30485           
30486
30487     },
30488
30489     // private
30490     initValue : Roo.emptyFn,
30491
30492     /**
30493      * Returns the checked state of the checkbox.
30494      * @return {Boolean} True if checked, else false
30495      */
30496     getValue : function(){
30497         return this.el.dom.value;
30498         
30499     },
30500
30501         // private
30502     onClick : function(e){ 
30503         //this.setChecked(!this.checked);
30504         Roo.get(e.target).toggleClass('x-menu-item-checked');
30505         this.refreshValue();
30506         //if(this.el.dom.checked != this.checked){
30507         //    this.setValue(this.el.dom.checked);
30508        // }
30509     },
30510     
30511     // private
30512     refreshValue : function()
30513     {
30514         var val = '';
30515         this.viewEl.select('img',true).each(function(e,i,n)  {
30516             val += e.is(".x-menu-item-checked") ? String(n) : '';
30517         });
30518         this.setValue(val, true);
30519     },
30520
30521     /**
30522      * Sets the checked state of the checkbox.
30523      * On is always based on a string comparison between inputValue and the param.
30524      * @param {Boolean/String} value - the value to set 
30525      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
30526      */
30527     setValue : function(v,suppressEvent){
30528         if (!this.el.dom) {
30529             return;
30530         }
30531         var old = this.el.dom.value ;
30532         this.el.dom.value = v;
30533         if (suppressEvent) {
30534             return ;
30535         }
30536          
30537         // update display..
30538         this.viewEl.select('img',true).each(function(e,i,n)  {
30539             
30540             var on = e.is(".x-menu-item-checked");
30541             var newv = v.indexOf(String(n)) > -1;
30542             if (on != newv) {
30543                 e.toggleClass('x-menu-item-checked');
30544             }
30545             
30546         });
30547         
30548         
30549         this.fireEvent('change', this, v, old);
30550         
30551         
30552     },
30553    
30554     // handle setting of hidden value by some other method!!?!?
30555     setFromHidden: function()
30556     {
30557         if(!this.el){
30558             return;
30559         }
30560         //console.log("SET FROM HIDDEN");
30561         //alert('setFrom hidden');
30562         this.setValue(this.el.dom.value);
30563     },
30564     
30565     onDestroy : function()
30566     {
30567         if(this.viewEl){
30568             Roo.get(this.viewEl).remove();
30569         }
30570          
30571         Roo.form.DayPicker.superclass.onDestroy.call(this);
30572     }
30573
30574 });/*
30575  * RooJS Library 1.1.1
30576  * Copyright(c) 2008-2011  Alan Knowles
30577  *
30578  * License - LGPL
30579  */
30580  
30581
30582 /**
30583  * @class Roo.form.ComboCheck
30584  * @extends Roo.form.ComboBox
30585  * A combobox for multiple select items.
30586  *
30587  * FIXME - could do with a reset button..
30588  * 
30589  * @constructor
30590  * Create a new ComboCheck
30591  * @param {Object} config Configuration options
30592  */
30593 Roo.form.ComboCheck = function(config){
30594     Roo.form.ComboCheck.superclass.constructor.call(this, config);
30595     // should verify some data...
30596     // like
30597     // hiddenName = required..
30598     // displayField = required
30599     // valudField == required
30600     var req= [ 'hiddenName', 'displayField', 'valueField' ];
30601     var _t = this;
30602     Roo.each(req, function(e) {
30603         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
30604             throw "Roo.form.ComboCheck : missing value for: " + e;
30605         }
30606     });
30607     
30608     
30609 };
30610
30611 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
30612      
30613      
30614     editable : false,
30615      
30616     selectedClass: 'x-menu-item-checked', 
30617     
30618     // private
30619     onRender : function(ct, position){
30620         var _t = this;
30621         
30622         
30623         
30624         if(!this.tpl){
30625             var cls = 'x-combo-list';
30626
30627             
30628             this.tpl =  new Roo.Template({
30629                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
30630                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
30631                    '<span>{' + this.displayField + '}</span>' +
30632                     '</div>' 
30633                 
30634             });
30635         }
30636  
30637         
30638         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
30639         this.view.singleSelect = false;
30640         this.view.multiSelect = true;
30641         this.view.toggleSelect = true;
30642         this.pageTb.add(new Roo.Toolbar.Fill(), {
30643             
30644             text: 'Done',
30645             handler: function()
30646             {
30647                 _t.collapse();
30648             }
30649         });
30650     },
30651     
30652     onViewOver : function(e, t){
30653         // do nothing...
30654         return;
30655         
30656     },
30657     
30658     onViewClick : function(doFocus,index){
30659         return;
30660         
30661     },
30662     select: function () {
30663         //Roo.log("SELECT CALLED");
30664     },
30665      
30666     selectByValue : function(xv, scrollIntoView){
30667         var ar = this.getValueArray();
30668         var sels = [];
30669         
30670         Roo.each(ar, function(v) {
30671             if(v === undefined || v === null){
30672                 return;
30673             }
30674             var r = this.findRecord(this.valueField, v);
30675             if(r){
30676                 sels.push(this.store.indexOf(r))
30677                 
30678             }
30679         },this);
30680         this.view.select(sels);
30681         return false;
30682     },
30683     
30684     
30685     
30686     onSelect : function(record, index){
30687        // Roo.log("onselect Called");
30688        // this is only called by the clear button now..
30689         this.view.clearSelections();
30690         this.setValue('[]');
30691         if (this.value != this.valueBefore) {
30692             this.fireEvent('change', this, this.value, this.valueBefore);
30693             this.valueBefore = this.value;
30694         }
30695     },
30696     getValueArray : function()
30697     {
30698         var ar = [] ;
30699         
30700         try {
30701             //Roo.log(this.value);
30702             if (typeof(this.value) == 'undefined') {
30703                 return [];
30704             }
30705             var ar = Roo.decode(this.value);
30706             return  ar instanceof Array ? ar : []; //?? valid?
30707             
30708         } catch(e) {
30709             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
30710             return [];
30711         }
30712          
30713     },
30714     expand : function ()
30715     {
30716         
30717         Roo.form.ComboCheck.superclass.expand.call(this);
30718         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
30719         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
30720         
30721
30722     },
30723     
30724     collapse : function(){
30725         Roo.form.ComboCheck.superclass.collapse.call(this);
30726         var sl = this.view.getSelectedIndexes();
30727         var st = this.store;
30728         var nv = [];
30729         var tv = [];
30730         var r;
30731         Roo.each(sl, function(i) {
30732             r = st.getAt(i);
30733             nv.push(r.get(this.valueField));
30734         },this);
30735         this.setValue(Roo.encode(nv));
30736         if (this.value != this.valueBefore) {
30737
30738             this.fireEvent('change', this, this.value, this.valueBefore);
30739             this.valueBefore = this.value;
30740         }
30741         
30742     },
30743     
30744     setValue : function(v){
30745         // Roo.log(v);
30746         this.value = v;
30747         
30748         var vals = this.getValueArray();
30749         var tv = [];
30750         Roo.each(vals, function(k) {
30751             var r = this.findRecord(this.valueField, k);
30752             if(r){
30753                 tv.push(r.data[this.displayField]);
30754             }else if(this.valueNotFoundText !== undefined){
30755                 tv.push( this.valueNotFoundText );
30756             }
30757         },this);
30758        // Roo.log(tv);
30759         
30760         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
30761         this.hiddenField.value = v;
30762         this.value = v;
30763     }
30764     
30765 });/*
30766  * Based on:
30767  * Ext JS Library 1.1.1
30768  * Copyright(c) 2006-2007, Ext JS, LLC.
30769  *
30770  * Originally Released Under LGPL - original licence link has changed is not relivant.
30771  *
30772  * Fork - LGPL
30773  * <script type="text/javascript">
30774  */
30775  
30776 /**
30777  * @class Roo.form.Signature
30778  * @extends Roo.form.Field
30779  * Signature field.  
30780  * @constructor
30781  * 
30782  * @param {Object} config Configuration options
30783  */
30784
30785 Roo.form.Signature = function(config){
30786     Roo.form.Signature.superclass.constructor.call(this, config);
30787     
30788     this.addEvents({// not in used??
30789          /**
30790          * @event confirm
30791          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
30792              * @param {Roo.form.Signature} combo This combo box
30793              */
30794         'confirm' : true,
30795         /**
30796          * @event reset
30797          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
30798              * @param {Roo.form.ComboBox} combo This combo box
30799              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
30800              */
30801         'reset' : true
30802     });
30803 };
30804
30805 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
30806     /**
30807      * @cfg {Object} labels Label to use when rendering a form.
30808      * defaults to 
30809      * labels : { 
30810      *      clear : "Clear",
30811      *      confirm : "Confirm"
30812      *  }
30813      */
30814     labels : { 
30815         clear : "Clear",
30816         confirm : "Confirm"
30817     },
30818     /**
30819      * @cfg {Number} width The signature panel width (defaults to 300)
30820      */
30821     width: 300,
30822     /**
30823      * @cfg {Number} height The signature panel height (defaults to 100)
30824      */
30825     height : 100,
30826     /**
30827      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
30828      */
30829     allowBlank : false,
30830     
30831     //private
30832     // {Object} signPanel The signature SVG panel element (defaults to {})
30833     signPanel : {},
30834     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
30835     isMouseDown : false,
30836     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
30837     isConfirmed : false,
30838     // {String} signatureTmp SVG mapping string (defaults to empty string)
30839     signatureTmp : '',
30840     
30841     
30842     defaultAutoCreate : { // modified by initCompnoent..
30843         tag: "input",
30844         type:"hidden"
30845     },
30846
30847     // private
30848     onRender : function(ct, position){
30849         
30850         Roo.form.Signature.superclass.onRender.call(this, ct, position);
30851         
30852         this.wrap = this.el.wrap({
30853             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
30854         });
30855         
30856         this.createToolbar(this);
30857         this.signPanel = this.wrap.createChild({
30858                 tag: 'div',
30859                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
30860             }, this.el
30861         );
30862             
30863         this.svgID = Roo.id();
30864         this.svgEl = this.signPanel.createChild({
30865               xmlns : 'http://www.w3.org/2000/svg',
30866               tag : 'svg',
30867               id : this.svgID + "-svg",
30868               width: this.width,
30869               height: this.height,
30870               viewBox: '0 0 '+this.width+' '+this.height,
30871               cn : [
30872                 {
30873                     tag: "rect",
30874                     id: this.svgID + "-svg-r",
30875                     width: this.width,
30876                     height: this.height,
30877                     fill: "#ffa"
30878                 },
30879                 {
30880                     tag: "line",
30881                     id: this.svgID + "-svg-l",
30882                     x1: "0", // start
30883                     y1: (this.height*0.8), // start set the line in 80% of height
30884                     x2: this.width, // end
30885                     y2: (this.height*0.8), // end set the line in 80% of height
30886                     'stroke': "#666",
30887                     'stroke-width': "1",
30888                     'stroke-dasharray': "3",
30889                     'shape-rendering': "crispEdges",
30890                     'pointer-events': "none"
30891                 },
30892                 {
30893                     tag: "path",
30894                     id: this.svgID + "-svg-p",
30895                     'stroke': "navy",
30896                     'stroke-width': "3",
30897                     'fill': "none",
30898                     'pointer-events': 'none'
30899                 }
30900               ]
30901         });
30902         this.createSVG();
30903         this.svgBox = this.svgEl.dom.getScreenCTM();
30904     },
30905     createSVG : function(){ 
30906         var svg = this.signPanel;
30907         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
30908         var t = this;
30909
30910         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
30911         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
30912         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
30913         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
30914         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
30915         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
30916         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
30917         
30918     },
30919     isTouchEvent : function(e){
30920         return e.type.match(/^touch/);
30921     },
30922     getCoords : function (e) {
30923         var pt    = this.svgEl.dom.createSVGPoint();
30924         pt.x = e.clientX; 
30925         pt.y = e.clientY;
30926         if (this.isTouchEvent(e)) {
30927             pt.x =  e.targetTouches[0].clientX 
30928             pt.y = e.targetTouches[0].clientY;
30929         }
30930         var a = this.svgEl.dom.getScreenCTM();
30931         var b = a.inverse();
30932         var mx = pt.matrixTransform(b);
30933         return mx.x + ',' + mx.y;
30934     },
30935     //mouse event headler 
30936     down : function (e) {
30937         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
30938         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
30939         
30940         this.isMouseDown = true;
30941         
30942         e.preventDefault();
30943     },
30944     move : function (e) {
30945         if (this.isMouseDown) {
30946             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
30947             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
30948         }
30949         
30950         e.preventDefault();
30951     },
30952     up : function (e) {
30953         this.isMouseDown = false;
30954         var sp = this.signatureTmp.split(' ');
30955         
30956         if(sp.length > 1){
30957             if(!sp[sp.length-2].match(/^L/)){
30958                 sp.pop();
30959                 sp.pop();
30960                 sp.push("");
30961                 this.signatureTmp = sp.join(" ");
30962             }
30963         }
30964         if(this.getValue() != this.signatureTmp){
30965             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
30966             this.isConfirmed = false;
30967         }
30968         e.preventDefault();
30969     },
30970     
30971     /**
30972      * Protected method that will not generally be called directly. It
30973      * is called when the editor creates its toolbar. Override this method if you need to
30974      * add custom toolbar buttons.
30975      * @param {HtmlEditor} editor
30976      */
30977     createToolbar : function(editor){
30978          function btn(id, toggle, handler){
30979             var xid = fid + '-'+ id ;
30980             return {
30981                 id : xid,
30982                 cmd : id,
30983                 cls : 'x-btn-icon x-edit-'+id,
30984                 enableToggle:toggle !== false,
30985                 scope: editor, // was editor...
30986                 handler:handler||editor.relayBtnCmd,
30987                 clickEvent:'mousedown',
30988                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
30989                 tabIndex:-1
30990             };
30991         }
30992         
30993         
30994         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
30995         this.tb = tb;
30996         this.tb.add(
30997            {
30998                 cls : ' x-signature-btn x-signature-'+id,
30999                 scope: editor, // was editor...
31000                 handler: this.reset,
31001                 clickEvent:'mousedown',
31002                 text: this.labels.clear
31003             },
31004             {
31005                  xtype : 'Fill',
31006                  xns: Roo.Toolbar
31007             }, 
31008             {
31009                 cls : '  x-signature-btn x-signature-'+id,
31010                 scope: editor, // was editor...
31011                 handler: this.confirmHandler,
31012                 clickEvent:'mousedown',
31013                 text: this.labels.confirm
31014             }
31015         );
31016     
31017     },
31018     //public
31019     /**
31020      * when user is clicked confirm then show this image.....
31021      * 
31022      * @return {String} Image Data URI
31023      */
31024     getImageDataURI : function(){
31025         var svg = this.svgEl.dom.parentNode.innerHTML;
31026         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
31027         return src; 
31028     },
31029     /**
31030      * 
31031      * @return {Boolean} this.isConfirmed
31032      */
31033     getConfirmed : function(){
31034         return this.isConfirmed;
31035     },
31036     /**
31037      * 
31038      * @return {Number} this.width
31039      */
31040     getWidth : function(){
31041         return this.width;
31042     },
31043     /**
31044      * 
31045      * @return {Number} this.height
31046      */
31047     getHeight : function(){
31048         return this.height;
31049     },
31050     // private
31051     getSignature : function(){
31052         return this.signatureTmp;
31053     },
31054     // private
31055     reset : function(){
31056         this.signatureTmp = '';
31057         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31058         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
31059         this.isConfirmed = false;
31060         Roo.form.Signature.superclass.reset.call(this);
31061     },
31062     setSignature : function(s){
31063         this.signatureTmp = s;
31064         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31065         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
31066         this.setValue(s);
31067         this.isConfirmed = false;
31068         Roo.form.Signature.superclass.reset.call(this);
31069     }, 
31070     test : function(){
31071 //        Roo.log(this.signPanel.dom.contentWindow.up())
31072     },
31073     //private
31074     setConfirmed : function(){
31075         
31076         
31077         
31078 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
31079     },
31080     // private
31081     confirmHandler : function(){
31082         if(!this.getSignature()){
31083             return;
31084         }
31085         
31086         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
31087         this.setValue(this.getSignature());
31088         this.isConfirmed = true;
31089         
31090         this.fireEvent('confirm', this);
31091     },
31092     // private
31093     // Subclasses should provide the validation implementation by overriding this
31094     validateValue : function(value){
31095         if(this.allowBlank){
31096             return true;
31097         }
31098         
31099         if(this.isConfirmed){
31100             return true;
31101         }
31102         return false;
31103     }
31104 });//<script type="text/javasscript">
31105  
31106
31107 /**
31108  * @class Roo.DDView
31109  * A DnD enabled version of Roo.View.
31110  * @param {Element/String} container The Element in which to create the View.
31111  * @param {String} tpl The template string used to create the markup for each element of the View
31112  * @param {Object} config The configuration properties. These include all the config options of
31113  * {@link Roo.View} plus some specific to this class.<br>
31114  * <p>
31115  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
31116  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
31117  * <p>
31118  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
31119 .x-view-drag-insert-above {
31120         border-top:1px dotted #3366cc;
31121 }
31122 .x-view-drag-insert-below {
31123         border-bottom:1px dotted #3366cc;
31124 }
31125 </code></pre>
31126  * 
31127  */
31128  
31129 Roo.DDView = function(container, tpl, config) {
31130     Roo.DDView.superclass.constructor.apply(this, arguments);
31131     this.getEl().setStyle("outline", "0px none");
31132     this.getEl().unselectable();
31133     if (this.dragGroup) {
31134                 this.setDraggable(this.dragGroup.split(","));
31135     }
31136     if (this.dropGroup) {
31137                 this.setDroppable(this.dropGroup.split(","));
31138     }
31139     if (this.deletable) {
31140         this.setDeletable();
31141     }
31142     this.isDirtyFlag = false;
31143         this.addEvents({
31144                 "drop" : true
31145         });
31146 };
31147
31148 Roo.extend(Roo.DDView, Roo.View, {
31149 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
31150 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
31151 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
31152 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
31153
31154         isFormField: true,
31155
31156         reset: Roo.emptyFn,
31157         
31158         clearInvalid: Roo.form.Field.prototype.clearInvalid,
31159
31160         validate: function() {
31161                 return true;
31162         },
31163         
31164         destroy: function() {
31165                 this.purgeListeners();
31166                 this.getEl.removeAllListeners();
31167                 this.getEl().remove();
31168                 if (this.dragZone) {
31169                         if (this.dragZone.destroy) {
31170                                 this.dragZone.destroy();
31171                         }
31172                 }
31173                 if (this.dropZone) {
31174                         if (this.dropZone.destroy) {
31175                                 this.dropZone.destroy();
31176                         }
31177                 }
31178         },
31179
31180 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
31181         getName: function() {
31182                 return this.name;
31183         },
31184
31185 /**     Loads the View from a JSON string representing the Records to put into the Store. */
31186         setValue: function(v) {
31187                 if (!this.store) {
31188                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
31189                 }
31190                 var data = {};
31191                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
31192                 this.store.proxy = new Roo.data.MemoryProxy(data);
31193                 this.store.load();
31194         },
31195
31196 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
31197         getValue: function() {
31198                 var result = '(';
31199                 this.store.each(function(rec) {
31200                         result += rec.id + ',';
31201                 });
31202                 return result.substr(0, result.length - 1) + ')';
31203         },
31204         
31205         getIds: function() {
31206                 var i = 0, result = new Array(this.store.getCount());
31207                 this.store.each(function(rec) {
31208                         result[i++] = rec.id;
31209                 });
31210                 return result;
31211         },
31212         
31213         isDirty: function() {
31214                 return this.isDirtyFlag;
31215         },
31216
31217 /**
31218  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
31219  *      whole Element becomes the target, and this causes the drop gesture to append.
31220  */
31221     getTargetFromEvent : function(e) {
31222                 var target = e.getTarget();
31223                 while ((target !== null) && (target.parentNode != this.el.dom)) {
31224                 target = target.parentNode;
31225                 }
31226                 if (!target) {
31227                         target = this.el.dom.lastChild || this.el.dom;
31228                 }
31229                 return target;
31230     },
31231
31232 /**
31233  *      Create the drag data which consists of an object which has the property "ddel" as
31234  *      the drag proxy element. 
31235  */
31236     getDragData : function(e) {
31237         var target = this.findItemFromChild(e.getTarget());
31238                 if(target) {
31239                         this.handleSelection(e);
31240                         var selNodes = this.getSelectedNodes();
31241             var dragData = {
31242                 source: this,
31243                 copy: this.copy || (this.allowCopy && e.ctrlKey),
31244                 nodes: selNodes,
31245                 records: []
31246                         };
31247                         var selectedIndices = this.getSelectedIndexes();
31248                         for (var i = 0; i < selectedIndices.length; i++) {
31249                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
31250                         }
31251                         if (selNodes.length == 1) {
31252                                 dragData.ddel = target.cloneNode(true); // the div element
31253                         } else {
31254                                 var div = document.createElement('div'); // create the multi element drag "ghost"
31255                                 div.className = 'multi-proxy';
31256                                 for (var i = 0, len = selNodes.length; i < len; i++) {
31257                                         div.appendChild(selNodes[i].cloneNode(true));
31258                                 }
31259                                 dragData.ddel = div;
31260                         }
31261             //console.log(dragData)
31262             //console.log(dragData.ddel.innerHTML)
31263                         return dragData;
31264                 }
31265         //console.log('nodragData')
31266                 return false;
31267     },
31268     
31269 /**     Specify to which ddGroup items in this DDView may be dragged. */
31270     setDraggable: function(ddGroup) {
31271         if (ddGroup instanceof Array) {
31272                 Roo.each(ddGroup, this.setDraggable, this);
31273                 return;
31274         }
31275         if (this.dragZone) {
31276                 this.dragZone.addToGroup(ddGroup);
31277         } else {
31278                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
31279                                 containerScroll: true,
31280                                 ddGroup: ddGroup 
31281
31282                         });
31283 //                      Draggability implies selection. DragZone's mousedown selects the element.
31284                         if (!this.multiSelect) { this.singleSelect = true; }
31285
31286 //                      Wire the DragZone's handlers up to methods in *this*
31287                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
31288                 }
31289     },
31290
31291 /**     Specify from which ddGroup this DDView accepts drops. */
31292     setDroppable: function(ddGroup) {
31293         if (ddGroup instanceof Array) {
31294                 Roo.each(ddGroup, this.setDroppable, this);
31295                 return;
31296         }
31297         if (this.dropZone) {
31298                 this.dropZone.addToGroup(ddGroup);
31299         } else {
31300                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
31301                                 containerScroll: true,
31302                                 ddGroup: ddGroup
31303                         });
31304
31305 //                      Wire the DropZone's handlers up to methods in *this*
31306                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
31307                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
31308                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
31309                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
31310                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
31311                 }
31312     },
31313
31314 /**     Decide whether to drop above or below a View node. */
31315     getDropPoint : function(e, n, dd){
31316         if (n == this.el.dom) { return "above"; }
31317                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
31318                 var c = t + (b - t) / 2;
31319                 var y = Roo.lib.Event.getPageY(e);
31320                 if(y <= c) {
31321                         return "above";
31322                 }else{
31323                         return "below";
31324                 }
31325     },
31326
31327     onNodeEnter : function(n, dd, e, data){
31328                 return false;
31329     },
31330     
31331     onNodeOver : function(n, dd, e, data){
31332                 var pt = this.getDropPoint(e, n, dd);
31333                 // set the insert point style on the target node
31334                 var dragElClass = this.dropNotAllowed;
31335                 if (pt) {
31336                         var targetElClass;
31337                         if (pt == "above"){
31338                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
31339                                 targetElClass = "x-view-drag-insert-above";
31340                         } else {
31341                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
31342                                 targetElClass = "x-view-drag-insert-below";
31343                         }
31344                         if (this.lastInsertClass != targetElClass){
31345                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
31346                                 this.lastInsertClass = targetElClass;
31347                         }
31348                 }
31349                 return dragElClass;
31350         },
31351
31352     onNodeOut : function(n, dd, e, data){
31353                 this.removeDropIndicators(n);
31354     },
31355
31356     onNodeDrop : function(n, dd, e, data){
31357         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
31358                 return false;
31359         }
31360         var pt = this.getDropPoint(e, n, dd);
31361                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
31362                 if (pt == "below") { insertAt++; }
31363                 for (var i = 0; i < data.records.length; i++) {
31364                         var r = data.records[i];
31365                         var dup = this.store.getById(r.id);
31366                         if (dup && (dd != this.dragZone)) {
31367                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
31368                         } else {
31369                                 if (data.copy) {
31370                                         this.store.insert(insertAt++, r.copy());
31371                                 } else {
31372                                         data.source.isDirtyFlag = true;
31373                                         r.store.remove(r);
31374                                         this.store.insert(insertAt++, r);
31375                                 }
31376                                 this.isDirtyFlag = true;
31377                         }
31378                 }
31379                 this.dragZone.cachedTarget = null;
31380                 return true;
31381     },
31382
31383     removeDropIndicators : function(n){
31384                 if(n){
31385                         Roo.fly(n).removeClass([
31386                                 "x-view-drag-insert-above",
31387                                 "x-view-drag-insert-below"]);
31388                         this.lastInsertClass = "_noclass";
31389                 }
31390     },
31391
31392 /**
31393  *      Utility method. Add a delete option to the DDView's context menu.
31394  *      @param {String} imageUrl The URL of the "delete" icon image.
31395  */
31396         setDeletable: function(imageUrl) {
31397                 if (!this.singleSelect && !this.multiSelect) {
31398                         this.singleSelect = true;
31399                 }
31400                 var c = this.getContextMenu();
31401                 this.contextMenu.on("itemclick", function(item) {
31402                         switch (item.id) {
31403                                 case "delete":
31404                                         this.remove(this.getSelectedIndexes());
31405                                         break;
31406                         }
31407                 }, this);
31408                 this.contextMenu.add({
31409                         icon: imageUrl,
31410                         id: "delete",
31411                         text: 'Delete'
31412                 });
31413         },
31414         
31415 /**     Return the context menu for this DDView. */
31416         getContextMenu: function() {
31417                 if (!this.contextMenu) {
31418 //                      Create the View's context menu
31419                         this.contextMenu = new Roo.menu.Menu({
31420                                 id: this.id + "-contextmenu"
31421                         });
31422                         this.el.on("contextmenu", this.showContextMenu, this);
31423                 }
31424                 return this.contextMenu;
31425         },
31426         
31427         disableContextMenu: function() {
31428                 if (this.contextMenu) {
31429                         this.el.un("contextmenu", this.showContextMenu, this);
31430                 }
31431         },
31432
31433         showContextMenu: function(e, item) {
31434         item = this.findItemFromChild(e.getTarget());
31435                 if (item) {
31436                         e.stopEvent();
31437                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
31438                         this.contextMenu.showAt(e.getXY());
31439             }
31440     },
31441
31442 /**
31443  *      Remove {@link Roo.data.Record}s at the specified indices.
31444  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
31445  */
31446     remove: function(selectedIndices) {
31447                 selectedIndices = [].concat(selectedIndices);
31448                 for (var i = 0; i < selectedIndices.length; i++) {
31449                         var rec = this.store.getAt(selectedIndices[i]);
31450                         this.store.remove(rec);
31451                 }
31452     },
31453
31454 /**
31455  *      Double click fires the event, but also, if this is draggable, and there is only one other
31456  *      related DropZone, it transfers the selected node.
31457  */
31458     onDblClick : function(e){
31459         var item = this.findItemFromChild(e.getTarget());
31460         if(item){
31461             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
31462                 return false;
31463             }
31464             if (this.dragGroup) {
31465                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
31466                     while (targets.indexOf(this.dropZone) > -1) {
31467                             targets.remove(this.dropZone);
31468                                 }
31469                     if (targets.length == 1) {
31470                                         this.dragZone.cachedTarget = null;
31471                         var el = Roo.get(targets[0].getEl());
31472                         var box = el.getBox(true);
31473                         targets[0].onNodeDrop(el.dom, {
31474                                 target: el.dom,
31475                                 xy: [box.x, box.y + box.height - 1]
31476                         }, null, this.getDragData(e));
31477                     }
31478                 }
31479         }
31480     },
31481     
31482     handleSelection: function(e) {
31483                 this.dragZone.cachedTarget = null;
31484         var item = this.findItemFromChild(e.getTarget());
31485         if (!item) {
31486                 this.clearSelections(true);
31487                 return;
31488         }
31489                 if (item && (this.multiSelect || this.singleSelect)){
31490                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
31491                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
31492                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
31493                                 this.unselect(item);
31494                         } else {
31495                                 this.select(item, this.multiSelect && e.ctrlKey);
31496                                 this.lastSelection = item;
31497                         }
31498                 }
31499     },
31500
31501     onItemClick : function(item, index, e){
31502                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
31503                         return false;
31504                 }
31505                 return true;
31506     },
31507
31508     unselect : function(nodeInfo, suppressEvent){
31509                 var node = this.getNode(nodeInfo);
31510                 if(node && this.isSelected(node)){
31511                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
31512                                 Roo.fly(node).removeClass(this.selectedClass);
31513                                 this.selections.remove(node);
31514                                 if(!suppressEvent){
31515                                         this.fireEvent("selectionchange", this, this.selections);
31516                                 }
31517                         }
31518                 }
31519     }
31520 });
31521 /*
31522  * Based on:
31523  * Ext JS Library 1.1.1
31524  * Copyright(c) 2006-2007, Ext JS, LLC.
31525  *
31526  * Originally Released Under LGPL - original licence link has changed is not relivant.
31527  *
31528  * Fork - LGPL
31529  * <script type="text/javascript">
31530  */
31531  
31532 /**
31533  * @class Roo.LayoutManager
31534  * @extends Roo.util.Observable
31535  * Base class for layout managers.
31536  */
31537 Roo.LayoutManager = function(container, config){
31538     Roo.LayoutManager.superclass.constructor.call(this);
31539     this.el = Roo.get(container);
31540     // ie scrollbar fix
31541     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
31542         document.body.scroll = "no";
31543     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
31544         this.el.position('relative');
31545     }
31546     this.id = this.el.id;
31547     this.el.addClass("x-layout-container");
31548     /** false to disable window resize monitoring @type Boolean */
31549     this.monitorWindowResize = true;
31550     this.regions = {};
31551     this.addEvents({
31552         /**
31553          * @event layout
31554          * Fires when a layout is performed. 
31555          * @param {Roo.LayoutManager} this
31556          */
31557         "layout" : true,
31558         /**
31559          * @event regionresized
31560          * Fires when the user resizes a region. 
31561          * @param {Roo.LayoutRegion} region The resized region
31562          * @param {Number} newSize The new size (width for east/west, height for north/south)
31563          */
31564         "regionresized" : true,
31565         /**
31566          * @event regioncollapsed
31567          * Fires when a region is collapsed. 
31568          * @param {Roo.LayoutRegion} region The collapsed region
31569          */
31570         "regioncollapsed" : true,
31571         /**
31572          * @event regionexpanded
31573          * Fires when a region is expanded.  
31574          * @param {Roo.LayoutRegion} region The expanded region
31575          */
31576         "regionexpanded" : true
31577     });
31578     this.updating = false;
31579     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
31580 };
31581
31582 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
31583     /**
31584      * Returns true if this layout is currently being updated
31585      * @return {Boolean}
31586      */
31587     isUpdating : function(){
31588         return this.updating; 
31589     },
31590     
31591     /**
31592      * Suspend the LayoutManager from doing auto-layouts while
31593      * making multiple add or remove calls
31594      */
31595     beginUpdate : function(){
31596         this.updating = true;    
31597     },
31598     
31599     /**
31600      * Restore auto-layouts and optionally disable the manager from performing a layout
31601      * @param {Boolean} noLayout true to disable a layout update 
31602      */
31603     endUpdate : function(noLayout){
31604         this.updating = false;
31605         if(!noLayout){
31606             this.layout();
31607         }    
31608     },
31609     
31610     layout: function(){
31611         
31612     },
31613     
31614     onRegionResized : function(region, newSize){
31615         this.fireEvent("regionresized", region, newSize);
31616         this.layout();
31617     },
31618     
31619     onRegionCollapsed : function(region){
31620         this.fireEvent("regioncollapsed", region);
31621     },
31622     
31623     onRegionExpanded : function(region){
31624         this.fireEvent("regionexpanded", region);
31625     },
31626         
31627     /**
31628      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
31629      * performs box-model adjustments.
31630      * @return {Object} The size as an object {width: (the width), height: (the height)}
31631      */
31632     getViewSize : function(){
31633         var size;
31634         if(this.el.dom != document.body){
31635             size = this.el.getSize();
31636         }else{
31637             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
31638         }
31639         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
31640         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
31641         return size;
31642     },
31643     
31644     /**
31645      * Returns the Element this layout is bound to.
31646      * @return {Roo.Element}
31647      */
31648     getEl : function(){
31649         return this.el;
31650     },
31651     
31652     /**
31653      * Returns the specified region.
31654      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
31655      * @return {Roo.LayoutRegion}
31656      */
31657     getRegion : function(target){
31658         return this.regions[target.toLowerCase()];
31659     },
31660     
31661     onWindowResize : function(){
31662         if(this.monitorWindowResize){
31663             this.layout();
31664         }
31665     }
31666 });/*
31667  * Based on:
31668  * Ext JS Library 1.1.1
31669  * Copyright(c) 2006-2007, Ext JS, LLC.
31670  *
31671  * Originally Released Under LGPL - original licence link has changed is not relivant.
31672  *
31673  * Fork - LGPL
31674  * <script type="text/javascript">
31675  */
31676 /**
31677  * @class Roo.BorderLayout
31678  * @extends Roo.LayoutManager
31679  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
31680  * please see: <br><br>
31681  * <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>
31682  * <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>
31683  * Example:
31684  <pre><code>
31685  var layout = new Roo.BorderLayout(document.body, {
31686     north: {
31687         initialSize: 25,
31688         titlebar: false
31689     },
31690     west: {
31691         split:true,
31692         initialSize: 200,
31693         minSize: 175,
31694         maxSize: 400,
31695         titlebar: true,
31696         collapsible: true
31697     },
31698     east: {
31699         split:true,
31700         initialSize: 202,
31701         minSize: 175,
31702         maxSize: 400,
31703         titlebar: true,
31704         collapsible: true
31705     },
31706     south: {
31707         split:true,
31708         initialSize: 100,
31709         minSize: 100,
31710         maxSize: 200,
31711         titlebar: true,
31712         collapsible: true
31713     },
31714     center: {
31715         titlebar: true,
31716         autoScroll:true,
31717         resizeTabs: true,
31718         minTabWidth: 50,
31719         preferredTabWidth: 150
31720     }
31721 });
31722
31723 // shorthand
31724 var CP = Roo.ContentPanel;
31725
31726 layout.beginUpdate();
31727 layout.add("north", new CP("north", "North"));
31728 layout.add("south", new CP("south", {title: "South", closable: true}));
31729 layout.add("west", new CP("west", {title: "West"}));
31730 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
31731 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
31732 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
31733 layout.getRegion("center").showPanel("center1");
31734 layout.endUpdate();
31735 </code></pre>
31736
31737 <b>The container the layout is rendered into can be either the body element or any other element.
31738 If it is not the body element, the container needs to either be an absolute positioned element,
31739 or you will need to add "position:relative" to the css of the container.  You will also need to specify
31740 the container size if it is not the body element.</b>
31741
31742 * @constructor
31743 * Create a new BorderLayout
31744 * @param {String/HTMLElement/Element} container The container this layout is bound to
31745 * @param {Object} config Configuration options
31746  */
31747 Roo.BorderLayout = function(container, config){
31748     config = config || {};
31749     Roo.BorderLayout.superclass.constructor.call(this, container, config);
31750     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
31751     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
31752         var target = this.factory.validRegions[i];
31753         if(config[target]){
31754             this.addRegion(target, config[target]);
31755         }
31756     }
31757 };
31758
31759 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
31760     /**
31761      * Creates and adds a new region if it doesn't already exist.
31762      * @param {String} target The target region key (north, south, east, west or center).
31763      * @param {Object} config The regions config object
31764      * @return {BorderLayoutRegion} The new region
31765      */
31766     addRegion : function(target, config){
31767         if(!this.regions[target]){
31768             var r = this.factory.create(target, this, config);
31769             this.bindRegion(target, r);
31770         }
31771         return this.regions[target];
31772     },
31773
31774     // private (kinda)
31775     bindRegion : function(name, r){
31776         this.regions[name] = r;
31777         r.on("visibilitychange", this.layout, this);
31778         r.on("paneladded", this.layout, this);
31779         r.on("panelremoved", this.layout, this);
31780         r.on("invalidated", this.layout, this);
31781         r.on("resized", this.onRegionResized, this);
31782         r.on("collapsed", this.onRegionCollapsed, this);
31783         r.on("expanded", this.onRegionExpanded, this);
31784     },
31785
31786     /**
31787      * Performs a layout update.
31788      */
31789     layout : function(){
31790         if(this.updating) return;
31791         var size = this.getViewSize();
31792         var w = size.width;
31793         var h = size.height;
31794         var centerW = w;
31795         var centerH = h;
31796         var centerY = 0;
31797         var centerX = 0;
31798         //var x = 0, y = 0;
31799
31800         var rs = this.regions;
31801         var north = rs["north"];
31802         var south = rs["south"]; 
31803         var west = rs["west"];
31804         var east = rs["east"];
31805         var center = rs["center"];
31806         //if(this.hideOnLayout){ // not supported anymore
31807             //c.el.setStyle("display", "none");
31808         //}
31809         if(north && north.isVisible()){
31810             var b = north.getBox();
31811             var m = north.getMargins();
31812             b.width = w - (m.left+m.right);
31813             b.x = m.left;
31814             b.y = m.top;
31815             centerY = b.height + b.y + m.bottom;
31816             centerH -= centerY;
31817             north.updateBox(this.safeBox(b));
31818         }
31819         if(south && south.isVisible()){
31820             var b = south.getBox();
31821             var m = south.getMargins();
31822             b.width = w - (m.left+m.right);
31823             b.x = m.left;
31824             var totalHeight = (b.height + m.top + m.bottom);
31825             b.y = h - totalHeight + m.top;
31826             centerH -= totalHeight;
31827             south.updateBox(this.safeBox(b));
31828         }
31829         if(west && west.isVisible()){
31830             var b = west.getBox();
31831             var m = west.getMargins();
31832             b.height = centerH - (m.top+m.bottom);
31833             b.x = m.left;
31834             b.y = centerY + m.top;
31835             var totalWidth = (b.width + m.left + m.right);
31836             centerX += totalWidth;
31837             centerW -= totalWidth;
31838             west.updateBox(this.safeBox(b));
31839         }
31840         if(east && east.isVisible()){
31841             var b = east.getBox();
31842             var m = east.getMargins();
31843             b.height = centerH - (m.top+m.bottom);
31844             var totalWidth = (b.width + m.left + m.right);
31845             b.x = w - totalWidth + m.left;
31846             b.y = centerY + m.top;
31847             centerW -= totalWidth;
31848             east.updateBox(this.safeBox(b));
31849         }
31850         if(center){
31851             var m = center.getMargins();
31852             var centerBox = {
31853                 x: centerX + m.left,
31854                 y: centerY + m.top,
31855                 width: centerW - (m.left+m.right),
31856                 height: centerH - (m.top+m.bottom)
31857             };
31858             //if(this.hideOnLayout){
31859                 //center.el.setStyle("display", "block");
31860             //}
31861             center.updateBox(this.safeBox(centerBox));
31862         }
31863         this.el.repaint();
31864         this.fireEvent("layout", this);
31865     },
31866
31867     // private
31868     safeBox : function(box){
31869         box.width = Math.max(0, box.width);
31870         box.height = Math.max(0, box.height);
31871         return box;
31872     },
31873
31874     /**
31875      * Adds a ContentPanel (or subclass) to this layout.
31876      * @param {String} target The target region key (north, south, east, west or center).
31877      * @param {Roo.ContentPanel} panel The panel to add
31878      * @return {Roo.ContentPanel} The added panel
31879      */
31880     add : function(target, panel){
31881          
31882         target = target.toLowerCase();
31883         return this.regions[target].add(panel);
31884     },
31885
31886     /**
31887      * Remove a ContentPanel (or subclass) to this layout.
31888      * @param {String} target The target region key (north, south, east, west or center).
31889      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
31890      * @return {Roo.ContentPanel} The removed panel
31891      */
31892     remove : function(target, panel){
31893         target = target.toLowerCase();
31894         return this.regions[target].remove(panel);
31895     },
31896
31897     /**
31898      * Searches all regions for a panel with the specified id
31899      * @param {String} panelId
31900      * @return {Roo.ContentPanel} The panel or null if it wasn't found
31901      */
31902     findPanel : function(panelId){
31903         var rs = this.regions;
31904         for(var target in rs){
31905             if(typeof rs[target] != "function"){
31906                 var p = rs[target].getPanel(panelId);
31907                 if(p){
31908                     return p;
31909                 }
31910             }
31911         }
31912         return null;
31913     },
31914
31915     /**
31916      * Searches all regions for a panel with the specified id and activates (shows) it.
31917      * @param {String/ContentPanel} panelId The panels id or the panel itself
31918      * @return {Roo.ContentPanel} The shown panel or null
31919      */
31920     showPanel : function(panelId) {
31921       var rs = this.regions;
31922       for(var target in rs){
31923          var r = rs[target];
31924          if(typeof r != "function"){
31925             if(r.hasPanel(panelId)){
31926                return r.showPanel(panelId);
31927             }
31928          }
31929       }
31930       return null;
31931    },
31932
31933    /**
31934      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
31935      * @param {Roo.state.Provider} provider (optional) An alternate state provider
31936      */
31937     restoreState : function(provider){
31938         if(!provider){
31939             provider = Roo.state.Manager;
31940         }
31941         var sm = new Roo.LayoutStateManager();
31942         sm.init(this, provider);
31943     },
31944
31945     /**
31946      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
31947      * object should contain properties for each region to add ContentPanels to, and each property's value should be
31948      * a valid ContentPanel config object.  Example:
31949      * <pre><code>
31950 // Create the main layout
31951 var layout = new Roo.BorderLayout('main-ct', {
31952     west: {
31953         split:true,
31954         minSize: 175,
31955         titlebar: true
31956     },
31957     center: {
31958         title:'Components'
31959     }
31960 }, 'main-ct');
31961
31962 // Create and add multiple ContentPanels at once via configs
31963 layout.batchAdd({
31964    west: {
31965        id: 'source-files',
31966        autoCreate:true,
31967        title:'Ext Source Files',
31968        autoScroll:true,
31969        fitToFrame:true
31970    },
31971    center : {
31972        el: cview,
31973        autoScroll:true,
31974        fitToFrame:true,
31975        toolbar: tb,
31976        resizeEl:'cbody'
31977    }
31978 });
31979 </code></pre>
31980      * @param {Object} regions An object containing ContentPanel configs by region name
31981      */
31982     batchAdd : function(regions){
31983         this.beginUpdate();
31984         for(var rname in regions){
31985             var lr = this.regions[rname];
31986             if(lr){
31987                 this.addTypedPanels(lr, regions[rname]);
31988             }
31989         }
31990         this.endUpdate();
31991     },
31992
31993     // private
31994     addTypedPanels : function(lr, ps){
31995         if(typeof ps == 'string'){
31996             lr.add(new Roo.ContentPanel(ps));
31997         }
31998         else if(ps instanceof Array){
31999             for(var i =0, len = ps.length; i < len; i++){
32000                 this.addTypedPanels(lr, ps[i]);
32001             }
32002         }
32003         else if(!ps.events){ // raw config?
32004             var el = ps.el;
32005             delete ps.el; // prevent conflict
32006             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
32007         }
32008         else {  // panel object assumed!
32009             lr.add(ps);
32010         }
32011     },
32012     /**
32013      * Adds a xtype elements to the layout.
32014      * <pre><code>
32015
32016 layout.addxtype({
32017        xtype : 'ContentPanel',
32018        region: 'west',
32019        items: [ .... ]
32020    }
32021 );
32022
32023 layout.addxtype({
32024         xtype : 'NestedLayoutPanel',
32025         region: 'west',
32026         layout: {
32027            center: { },
32028            west: { }   
32029         },
32030         items : [ ... list of content panels or nested layout panels.. ]
32031    }
32032 );
32033 </code></pre>
32034      * @param {Object} cfg Xtype definition of item to add.
32035      */
32036     addxtype : function(cfg)
32037     {
32038         // basically accepts a pannel...
32039         // can accept a layout region..!?!?
32040         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
32041         
32042         if (!cfg.xtype.match(/Panel$/)) {
32043             return false;
32044         }
32045         var ret = false;
32046         
32047         if (typeof(cfg.region) == 'undefined') {
32048             Roo.log("Failed to add Panel, region was not set");
32049             Roo.log(cfg);
32050             return false;
32051         }
32052         var region = cfg.region;
32053         delete cfg.region;
32054         
32055           
32056         var xitems = [];
32057         if (cfg.items) {
32058             xitems = cfg.items;
32059             delete cfg.items;
32060         }
32061         var nb = false;
32062         
32063         switch(cfg.xtype) 
32064         {
32065             case 'ContentPanel':  // ContentPanel (el, cfg)
32066             case 'ScrollPanel':  // ContentPanel (el, cfg)
32067             case 'ViewPanel': 
32068                 if(cfg.autoCreate) {
32069                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32070                 } else {
32071                     var el = this.el.createChild();
32072                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
32073                 }
32074                 
32075                 this.add(region, ret);
32076                 break;
32077             
32078             
32079             case 'TreePanel': // our new panel!
32080                 cfg.el = this.el.createChild();
32081                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32082                 this.add(region, ret);
32083                 break;
32084             
32085             case 'NestedLayoutPanel': 
32086                 // create a new Layout (which is  a Border Layout...
32087                 var el = this.el.createChild();
32088                 var clayout = cfg.layout;
32089                 delete cfg.layout;
32090                 clayout.items   = clayout.items  || [];
32091                 // replace this exitems with the clayout ones..
32092                 xitems = clayout.items;
32093                  
32094                 
32095                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
32096                     cfg.background = false;
32097                 }
32098                 var layout = new Roo.BorderLayout(el, clayout);
32099                 
32100                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
32101                 //console.log('adding nested layout panel '  + cfg.toSource());
32102                 this.add(region, ret);
32103                 nb = {}; /// find first...
32104                 break;
32105                 
32106             case 'GridPanel': 
32107             
32108                 // needs grid and region
32109                 
32110                 //var el = this.getRegion(region).el.createChild();
32111                 var el = this.el.createChild();
32112                 // create the grid first...
32113                 
32114                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
32115                 delete cfg.grid;
32116                 if (region == 'center' && this.active ) {
32117                     cfg.background = false;
32118                 }
32119                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
32120                 
32121                 this.add(region, ret);
32122                 if (cfg.background) {
32123                     ret.on('activate', function(gp) {
32124                         if (!gp.grid.rendered) {
32125                             gp.grid.render();
32126                         }
32127                     });
32128                 } else {
32129                     grid.render();
32130                 }
32131                 break;
32132            
32133            
32134            
32135                 
32136                 
32137                 
32138             default: 
32139                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
32140                 return null;
32141              // GridPanel (grid, cfg)
32142             
32143         }
32144         this.beginUpdate();
32145         // add children..
32146         var region = '';
32147         var abn = {};
32148         Roo.each(xitems, function(i)  {
32149             region = nb && i.region ? i.region : false;
32150             
32151             var add = ret.addxtype(i);
32152            
32153             if (region) {
32154                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
32155                 if (!i.background) {
32156                     abn[region] = nb[region] ;
32157                 }
32158             }
32159             
32160         });
32161         this.endUpdate();
32162
32163         // make the last non-background panel active..
32164         //if (nb) { Roo.log(abn); }
32165         if (nb) {
32166             
32167             for(var r in abn) {
32168                 region = this.getRegion(r);
32169                 if (region) {
32170                     // tried using nb[r], but it does not work..
32171                      
32172                     region.showPanel(abn[r]);
32173                    
32174                 }
32175             }
32176         }
32177         return ret;
32178         
32179     }
32180 });
32181
32182 /**
32183  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
32184  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
32185  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
32186  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
32187  * <pre><code>
32188 // shorthand
32189 var CP = Roo.ContentPanel;
32190
32191 var layout = Roo.BorderLayout.create({
32192     north: {
32193         initialSize: 25,
32194         titlebar: false,
32195         panels: [new CP("north", "North")]
32196     },
32197     west: {
32198         split:true,
32199         initialSize: 200,
32200         minSize: 175,
32201         maxSize: 400,
32202         titlebar: true,
32203         collapsible: true,
32204         panels: [new CP("west", {title: "West"})]
32205     },
32206     east: {
32207         split:true,
32208         initialSize: 202,
32209         minSize: 175,
32210         maxSize: 400,
32211         titlebar: true,
32212         collapsible: true,
32213         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
32214     },
32215     south: {
32216         split:true,
32217         initialSize: 100,
32218         minSize: 100,
32219         maxSize: 200,
32220         titlebar: true,
32221         collapsible: true,
32222         panels: [new CP("south", {title: "South", closable: true})]
32223     },
32224     center: {
32225         titlebar: true,
32226         autoScroll:true,
32227         resizeTabs: true,
32228         minTabWidth: 50,
32229         preferredTabWidth: 150,
32230         panels: [
32231             new CP("center1", {title: "Close Me", closable: true}),
32232             new CP("center2", {title: "Center Panel", closable: false})
32233         ]
32234     }
32235 }, document.body);
32236
32237 layout.getRegion("center").showPanel("center1");
32238 </code></pre>
32239  * @param config
32240  * @param targetEl
32241  */
32242 Roo.BorderLayout.create = function(config, targetEl){
32243     var layout = new Roo.BorderLayout(targetEl || document.body, config);
32244     layout.beginUpdate();
32245     var regions = Roo.BorderLayout.RegionFactory.validRegions;
32246     for(var j = 0, jlen = regions.length; j < jlen; j++){
32247         var lr = regions[j];
32248         if(layout.regions[lr] && config[lr].panels){
32249             var r = layout.regions[lr];
32250             var ps = config[lr].panels;
32251             layout.addTypedPanels(r, ps);
32252         }
32253     }
32254     layout.endUpdate();
32255     return layout;
32256 };
32257
32258 // private
32259 Roo.BorderLayout.RegionFactory = {
32260     // private
32261     validRegions : ["north","south","east","west","center"],
32262
32263     // private
32264     create : function(target, mgr, config){
32265         target = target.toLowerCase();
32266         if(config.lightweight || config.basic){
32267             return new Roo.BasicLayoutRegion(mgr, config, target);
32268         }
32269         switch(target){
32270             case "north":
32271                 return new Roo.NorthLayoutRegion(mgr, config);
32272             case "south":
32273                 return new Roo.SouthLayoutRegion(mgr, config);
32274             case "east":
32275                 return new Roo.EastLayoutRegion(mgr, config);
32276             case "west":
32277                 return new Roo.WestLayoutRegion(mgr, config);
32278             case "center":
32279                 return new Roo.CenterLayoutRegion(mgr, config);
32280         }
32281         throw 'Layout region "'+target+'" not supported.';
32282     }
32283 };/*
32284  * Based on:
32285  * Ext JS Library 1.1.1
32286  * Copyright(c) 2006-2007, Ext JS, LLC.
32287  *
32288  * Originally Released Under LGPL - original licence link has changed is not relivant.
32289  *
32290  * Fork - LGPL
32291  * <script type="text/javascript">
32292  */
32293  
32294 /**
32295  * @class Roo.BasicLayoutRegion
32296  * @extends Roo.util.Observable
32297  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
32298  * and does not have a titlebar, tabs or any other features. All it does is size and position 
32299  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
32300  */
32301 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
32302     this.mgr = mgr;
32303     this.position  = pos;
32304     this.events = {
32305         /**
32306          * @scope Roo.BasicLayoutRegion
32307          */
32308         
32309         /**
32310          * @event beforeremove
32311          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
32312          * @param {Roo.LayoutRegion} this
32313          * @param {Roo.ContentPanel} panel The panel
32314          * @param {Object} e The cancel event object
32315          */
32316         "beforeremove" : true,
32317         /**
32318          * @event invalidated
32319          * Fires when the layout for this region is changed.
32320          * @param {Roo.LayoutRegion} this
32321          */
32322         "invalidated" : true,
32323         /**
32324          * @event visibilitychange
32325          * Fires when this region is shown or hidden 
32326          * @param {Roo.LayoutRegion} this
32327          * @param {Boolean} visibility true or false
32328          */
32329         "visibilitychange" : true,
32330         /**
32331          * @event paneladded
32332          * Fires when a panel is added. 
32333          * @param {Roo.LayoutRegion} this
32334          * @param {Roo.ContentPanel} panel The panel
32335          */
32336         "paneladded" : true,
32337         /**
32338          * @event panelremoved
32339          * Fires when a panel is removed. 
32340          * @param {Roo.LayoutRegion} this
32341          * @param {Roo.ContentPanel} panel The panel
32342          */
32343         "panelremoved" : true,
32344         /**
32345          * @event collapsed
32346          * Fires when this region is collapsed.
32347          * @param {Roo.LayoutRegion} this
32348          */
32349         "collapsed" : true,
32350         /**
32351          * @event expanded
32352          * Fires when this region is expanded.
32353          * @param {Roo.LayoutRegion} this
32354          */
32355         "expanded" : true,
32356         /**
32357          * @event slideshow
32358          * Fires when this region is slid into view.
32359          * @param {Roo.LayoutRegion} this
32360          */
32361         "slideshow" : true,
32362         /**
32363          * @event slidehide
32364          * Fires when this region slides out of view. 
32365          * @param {Roo.LayoutRegion} this
32366          */
32367         "slidehide" : true,
32368         /**
32369          * @event panelactivated
32370          * Fires when a panel is activated. 
32371          * @param {Roo.LayoutRegion} this
32372          * @param {Roo.ContentPanel} panel The activated panel
32373          */
32374         "panelactivated" : true,
32375         /**
32376          * @event resized
32377          * Fires when the user resizes this region. 
32378          * @param {Roo.LayoutRegion} this
32379          * @param {Number} newSize The new size (width for east/west, height for north/south)
32380          */
32381         "resized" : true
32382     };
32383     /** A collection of panels in this region. @type Roo.util.MixedCollection */
32384     this.panels = new Roo.util.MixedCollection();
32385     this.panels.getKey = this.getPanelId.createDelegate(this);
32386     this.box = null;
32387     this.activePanel = null;
32388     // ensure listeners are added...
32389     
32390     if (config.listeners || config.events) {
32391         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
32392             listeners : config.listeners || {},
32393             events : config.events || {}
32394         });
32395     }
32396     
32397     if(skipConfig !== true){
32398         this.applyConfig(config);
32399     }
32400 };
32401
32402 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
32403     getPanelId : function(p){
32404         return p.getId();
32405     },
32406     
32407     applyConfig : function(config){
32408         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
32409         this.config = config;
32410         
32411     },
32412     
32413     /**
32414      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
32415      * the width, for horizontal (north, south) the height.
32416      * @param {Number} newSize The new width or height
32417      */
32418     resizeTo : function(newSize){
32419         var el = this.el ? this.el :
32420                  (this.activePanel ? this.activePanel.getEl() : null);
32421         if(el){
32422             switch(this.position){
32423                 case "east":
32424                 case "west":
32425                     el.setWidth(newSize);
32426                     this.fireEvent("resized", this, newSize);
32427                 break;
32428                 case "north":
32429                 case "south":
32430                     el.setHeight(newSize);
32431                     this.fireEvent("resized", this, newSize);
32432                 break;                
32433             }
32434         }
32435     },
32436     
32437     getBox : function(){
32438         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
32439     },
32440     
32441     getMargins : function(){
32442         return this.margins;
32443     },
32444     
32445     updateBox : function(box){
32446         this.box = box;
32447         var el = this.activePanel.getEl();
32448         el.dom.style.left = box.x + "px";
32449         el.dom.style.top = box.y + "px";
32450         this.activePanel.setSize(box.width, box.height);
32451     },
32452     
32453     /**
32454      * Returns the container element for this region.
32455      * @return {Roo.Element}
32456      */
32457     getEl : function(){
32458         return this.activePanel;
32459     },
32460     
32461     /**
32462      * Returns true if this region is currently visible.
32463      * @return {Boolean}
32464      */
32465     isVisible : function(){
32466         return this.activePanel ? true : false;
32467     },
32468     
32469     setActivePanel : function(panel){
32470         panel = this.getPanel(panel);
32471         if(this.activePanel && this.activePanel != panel){
32472             this.activePanel.setActiveState(false);
32473             this.activePanel.getEl().setLeftTop(-10000,-10000);
32474         }
32475         this.activePanel = panel;
32476         panel.setActiveState(true);
32477         if(this.box){
32478             panel.setSize(this.box.width, this.box.height);
32479         }
32480         this.fireEvent("panelactivated", this, panel);
32481         this.fireEvent("invalidated");
32482     },
32483     
32484     /**
32485      * Show the specified panel.
32486      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
32487      * @return {Roo.ContentPanel} The shown panel or null
32488      */
32489     showPanel : function(panel){
32490         if(panel = this.getPanel(panel)){
32491             this.setActivePanel(panel);
32492         }
32493         return panel;
32494     },
32495     
32496     /**
32497      * Get the active panel for this region.
32498      * @return {Roo.ContentPanel} The active panel or null
32499      */
32500     getActivePanel : function(){
32501         return this.activePanel;
32502     },
32503     
32504     /**
32505      * Add the passed ContentPanel(s)
32506      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32507      * @return {Roo.ContentPanel} The panel added (if only one was added)
32508      */
32509     add : function(panel){
32510         if(arguments.length > 1){
32511             for(var i = 0, len = arguments.length; i < len; i++) {
32512                 this.add(arguments[i]);
32513             }
32514             return null;
32515         }
32516         if(this.hasPanel(panel)){
32517             this.showPanel(panel);
32518             return panel;
32519         }
32520         var el = panel.getEl();
32521         if(el.dom.parentNode != this.mgr.el.dom){
32522             this.mgr.el.dom.appendChild(el.dom);
32523         }
32524         if(panel.setRegion){
32525             panel.setRegion(this);
32526         }
32527         this.panels.add(panel);
32528         el.setStyle("position", "absolute");
32529         if(!panel.background){
32530             this.setActivePanel(panel);
32531             if(this.config.initialSize && this.panels.getCount()==1){
32532                 this.resizeTo(this.config.initialSize);
32533             }
32534         }
32535         this.fireEvent("paneladded", this, panel);
32536         return panel;
32537     },
32538     
32539     /**
32540      * Returns true if the panel is in this region.
32541      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32542      * @return {Boolean}
32543      */
32544     hasPanel : function(panel){
32545         if(typeof panel == "object"){ // must be panel obj
32546             panel = panel.getId();
32547         }
32548         return this.getPanel(panel) ? true : false;
32549     },
32550     
32551     /**
32552      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
32553      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32554      * @param {Boolean} preservePanel Overrides the config preservePanel option
32555      * @return {Roo.ContentPanel} The panel that was removed
32556      */
32557     remove : function(panel, preservePanel){
32558         panel = this.getPanel(panel);
32559         if(!panel){
32560             return null;
32561         }
32562         var e = {};
32563         this.fireEvent("beforeremove", this, panel, e);
32564         if(e.cancel === true){
32565             return null;
32566         }
32567         var panelId = panel.getId();
32568         this.panels.removeKey(panelId);
32569         return panel;
32570     },
32571     
32572     /**
32573      * Returns the panel specified or null if it's not in this region.
32574      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32575      * @return {Roo.ContentPanel}
32576      */
32577     getPanel : function(id){
32578         if(typeof id == "object"){ // must be panel obj
32579             return id;
32580         }
32581         return this.panels.get(id);
32582     },
32583     
32584     /**
32585      * Returns this regions position (north/south/east/west/center).
32586      * @return {String} 
32587      */
32588     getPosition: function(){
32589         return this.position;    
32590     }
32591 });/*
32592  * Based on:
32593  * Ext JS Library 1.1.1
32594  * Copyright(c) 2006-2007, Ext JS, LLC.
32595  *
32596  * Originally Released Under LGPL - original licence link has changed is not relivant.
32597  *
32598  * Fork - LGPL
32599  * <script type="text/javascript">
32600  */
32601  
32602 /**
32603  * @class Roo.LayoutRegion
32604  * @extends Roo.BasicLayoutRegion
32605  * This class represents a region in a layout manager.
32606  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
32607  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
32608  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
32609  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
32610  * @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})
32611  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
32612  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
32613  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
32614  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
32615  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
32616  * @cfg {String}    title           The title for the region (overrides panel titles)
32617  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
32618  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
32619  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
32620  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
32621  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
32622  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
32623  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
32624  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
32625  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
32626  * @cfg {Boolean}   showPin         True to show a pin button
32627  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
32628  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
32629  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
32630  * @cfg {Number}    width           For East/West panels
32631  * @cfg {Number}    height          For North/South panels
32632  * @cfg {Boolean}   split           To show the splitter
32633  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
32634  */
32635 Roo.LayoutRegion = function(mgr, config, pos){
32636     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
32637     var dh = Roo.DomHelper;
32638     /** This region's container element 
32639     * @type Roo.Element */
32640     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
32641     /** This region's title element 
32642     * @type Roo.Element */
32643
32644     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
32645         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
32646         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
32647     ]}, true);
32648     this.titleEl.enableDisplayMode();
32649     /** This region's title text element 
32650     * @type HTMLElement */
32651     this.titleTextEl = this.titleEl.dom.firstChild;
32652     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
32653     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
32654     this.closeBtn.enableDisplayMode();
32655     this.closeBtn.on("click", this.closeClicked, this);
32656     this.closeBtn.hide();
32657
32658     this.createBody(config);
32659     this.visible = true;
32660     this.collapsed = false;
32661
32662     if(config.hideWhenEmpty){
32663         this.hide();
32664         this.on("paneladded", this.validateVisibility, this);
32665         this.on("panelremoved", this.validateVisibility, this);
32666     }
32667     this.applyConfig(config);
32668 };
32669
32670 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
32671
32672     createBody : function(){
32673         /** This region's body element 
32674         * @type Roo.Element */
32675         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
32676     },
32677
32678     applyConfig : function(c){
32679         if(c.collapsible && this.position != "center" && !this.collapsedEl){
32680             var dh = Roo.DomHelper;
32681             if(c.titlebar !== false){
32682                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
32683                 this.collapseBtn.on("click", this.collapse, this);
32684                 this.collapseBtn.enableDisplayMode();
32685
32686                 if(c.showPin === true || this.showPin){
32687                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
32688                     this.stickBtn.enableDisplayMode();
32689                     this.stickBtn.on("click", this.expand, this);
32690                     this.stickBtn.hide();
32691                 }
32692             }
32693             /** This region's collapsed element
32694             * @type Roo.Element */
32695             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
32696                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
32697             ]}, true);
32698             if(c.floatable !== false){
32699                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
32700                this.collapsedEl.on("click", this.collapseClick, this);
32701             }
32702
32703             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
32704                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
32705                    id: "message", unselectable: "on", style:{"float":"left"}});
32706                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
32707              }
32708             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
32709             this.expandBtn.on("click", this.expand, this);
32710         }
32711         if(this.collapseBtn){
32712             this.collapseBtn.setVisible(c.collapsible == true);
32713         }
32714         this.cmargins = c.cmargins || this.cmargins ||
32715                          (this.position == "west" || this.position == "east" ?
32716                              {top: 0, left: 2, right:2, bottom: 0} :
32717                              {top: 2, left: 0, right:0, bottom: 2});
32718         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
32719         this.bottomTabs = c.tabPosition != "top";
32720         this.autoScroll = c.autoScroll || false;
32721         if(this.autoScroll){
32722             this.bodyEl.setStyle("overflow", "auto");
32723         }else{
32724             this.bodyEl.setStyle("overflow", "hidden");
32725         }
32726         //if(c.titlebar !== false){
32727             if((!c.titlebar && !c.title) || c.titlebar === false){
32728                 this.titleEl.hide();
32729             }else{
32730                 this.titleEl.show();
32731                 if(c.title){
32732                     this.titleTextEl.innerHTML = c.title;
32733                 }
32734             }
32735         //}
32736         this.duration = c.duration || .30;
32737         this.slideDuration = c.slideDuration || .45;
32738         this.config = c;
32739         if(c.collapsed){
32740             this.collapse(true);
32741         }
32742         if(c.hidden){
32743             this.hide();
32744         }
32745     },
32746     /**
32747      * Returns true if this region is currently visible.
32748      * @return {Boolean}
32749      */
32750     isVisible : function(){
32751         return this.visible;
32752     },
32753
32754     /**
32755      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
32756      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
32757      */
32758     setCollapsedTitle : function(title){
32759         title = title || "&#160;";
32760         if(this.collapsedTitleTextEl){
32761             this.collapsedTitleTextEl.innerHTML = title;
32762         }
32763     },
32764
32765     getBox : function(){
32766         var b;
32767         if(!this.collapsed){
32768             b = this.el.getBox(false, true);
32769         }else{
32770             b = this.collapsedEl.getBox(false, true);
32771         }
32772         return b;
32773     },
32774
32775     getMargins : function(){
32776         return this.collapsed ? this.cmargins : this.margins;
32777     },
32778
32779     highlight : function(){
32780         this.el.addClass("x-layout-panel-dragover");
32781     },
32782
32783     unhighlight : function(){
32784         this.el.removeClass("x-layout-panel-dragover");
32785     },
32786
32787     updateBox : function(box){
32788         this.box = box;
32789         if(!this.collapsed){
32790             this.el.dom.style.left = box.x + "px";
32791             this.el.dom.style.top = box.y + "px";
32792             this.updateBody(box.width, box.height);
32793         }else{
32794             this.collapsedEl.dom.style.left = box.x + "px";
32795             this.collapsedEl.dom.style.top = box.y + "px";
32796             this.collapsedEl.setSize(box.width, box.height);
32797         }
32798         if(this.tabs){
32799             this.tabs.autoSizeTabs();
32800         }
32801     },
32802
32803     updateBody : function(w, h){
32804         if(w !== null){
32805             this.el.setWidth(w);
32806             w -= this.el.getBorderWidth("rl");
32807             if(this.config.adjustments){
32808                 w += this.config.adjustments[0];
32809             }
32810         }
32811         if(h !== null){
32812             this.el.setHeight(h);
32813             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
32814             h -= this.el.getBorderWidth("tb");
32815             if(this.config.adjustments){
32816                 h += this.config.adjustments[1];
32817             }
32818             this.bodyEl.setHeight(h);
32819             if(this.tabs){
32820                 h = this.tabs.syncHeight(h);
32821             }
32822         }
32823         if(this.panelSize){
32824             w = w !== null ? w : this.panelSize.width;
32825             h = h !== null ? h : this.panelSize.height;
32826         }
32827         if(this.activePanel){
32828             var el = this.activePanel.getEl();
32829             w = w !== null ? w : el.getWidth();
32830             h = h !== null ? h : el.getHeight();
32831             this.panelSize = {width: w, height: h};
32832             this.activePanel.setSize(w, h);
32833         }
32834         if(Roo.isIE && this.tabs){
32835             this.tabs.el.repaint();
32836         }
32837     },
32838
32839     /**
32840      * Returns the container element for this region.
32841      * @return {Roo.Element}
32842      */
32843     getEl : function(){
32844         return this.el;
32845     },
32846
32847     /**
32848      * Hides this region.
32849      */
32850     hide : function(){
32851         if(!this.collapsed){
32852             this.el.dom.style.left = "-2000px";
32853             this.el.hide();
32854         }else{
32855             this.collapsedEl.dom.style.left = "-2000px";
32856             this.collapsedEl.hide();
32857         }
32858         this.visible = false;
32859         this.fireEvent("visibilitychange", this, false);
32860     },
32861
32862     /**
32863      * Shows this region if it was previously hidden.
32864      */
32865     show : function(){
32866         if(!this.collapsed){
32867             this.el.show();
32868         }else{
32869             this.collapsedEl.show();
32870         }
32871         this.visible = true;
32872         this.fireEvent("visibilitychange", this, true);
32873     },
32874
32875     closeClicked : function(){
32876         if(this.activePanel){
32877             this.remove(this.activePanel);
32878         }
32879     },
32880
32881     collapseClick : function(e){
32882         if(this.isSlid){
32883            e.stopPropagation();
32884            this.slideIn();
32885         }else{
32886            e.stopPropagation();
32887            this.slideOut();
32888         }
32889     },
32890
32891     /**
32892      * Collapses this region.
32893      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
32894      */
32895     collapse : function(skipAnim){
32896         if(this.collapsed) return;
32897         this.collapsed = true;
32898         if(this.split){
32899             this.split.el.hide();
32900         }
32901         if(this.config.animate && skipAnim !== true){
32902             this.fireEvent("invalidated", this);
32903             this.animateCollapse();
32904         }else{
32905             this.el.setLocation(-20000,-20000);
32906             this.el.hide();
32907             this.collapsedEl.show();
32908             this.fireEvent("collapsed", this);
32909             this.fireEvent("invalidated", this);
32910         }
32911     },
32912
32913     animateCollapse : function(){
32914         // overridden
32915     },
32916
32917     /**
32918      * Expands this region if it was previously collapsed.
32919      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
32920      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
32921      */
32922     expand : function(e, skipAnim){
32923         if(e) e.stopPropagation();
32924         if(!this.collapsed || this.el.hasActiveFx()) return;
32925         if(this.isSlid){
32926             this.afterSlideIn();
32927             skipAnim = true;
32928         }
32929         this.collapsed = false;
32930         if(this.config.animate && skipAnim !== true){
32931             this.animateExpand();
32932         }else{
32933             this.el.show();
32934             if(this.split){
32935                 this.split.el.show();
32936             }
32937             this.collapsedEl.setLocation(-2000,-2000);
32938             this.collapsedEl.hide();
32939             this.fireEvent("invalidated", this);
32940             this.fireEvent("expanded", this);
32941         }
32942     },
32943
32944     animateExpand : function(){
32945         // overridden
32946     },
32947
32948     initTabs : function()
32949     {
32950         this.bodyEl.setStyle("overflow", "hidden");
32951         var ts = new Roo.TabPanel(
32952                 this.bodyEl.dom,
32953                 {
32954                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
32955                     disableTooltips: this.config.disableTabTips,
32956                     toolbar : this.config.toolbar
32957                 }
32958         );
32959         if(this.config.hideTabs){
32960             ts.stripWrap.setDisplayed(false);
32961         }
32962         this.tabs = ts;
32963         ts.resizeTabs = this.config.resizeTabs === true;
32964         ts.minTabWidth = this.config.minTabWidth || 40;
32965         ts.maxTabWidth = this.config.maxTabWidth || 250;
32966         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
32967         ts.monitorResize = false;
32968         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
32969         ts.bodyEl.addClass('x-layout-tabs-body');
32970         this.panels.each(this.initPanelAsTab, this);
32971     },
32972
32973     initPanelAsTab : function(panel){
32974         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
32975                     this.config.closeOnTab && panel.isClosable());
32976         if(panel.tabTip !== undefined){
32977             ti.setTooltip(panel.tabTip);
32978         }
32979         ti.on("activate", function(){
32980               this.setActivePanel(panel);
32981         }, this);
32982         if(this.config.closeOnTab){
32983             ti.on("beforeclose", function(t, e){
32984                 e.cancel = true;
32985                 this.remove(panel);
32986             }, this);
32987         }
32988         return ti;
32989     },
32990
32991     updatePanelTitle : function(panel, title){
32992         if(this.activePanel == panel){
32993             this.updateTitle(title);
32994         }
32995         if(this.tabs){
32996             var ti = this.tabs.getTab(panel.getEl().id);
32997             ti.setText(title);
32998             if(panel.tabTip !== undefined){
32999                 ti.setTooltip(panel.tabTip);
33000             }
33001         }
33002     },
33003
33004     updateTitle : function(title){
33005         if(this.titleTextEl && !this.config.title){
33006             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
33007         }
33008     },
33009
33010     setActivePanel : function(panel){
33011         panel = this.getPanel(panel);
33012         if(this.activePanel && this.activePanel != panel){
33013             this.activePanel.setActiveState(false);
33014         }
33015         this.activePanel = panel;
33016         panel.setActiveState(true);
33017         if(this.panelSize){
33018             panel.setSize(this.panelSize.width, this.panelSize.height);
33019         }
33020         if(this.closeBtn){
33021             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
33022         }
33023         this.updateTitle(panel.getTitle());
33024         if(this.tabs){
33025             this.fireEvent("invalidated", this);
33026         }
33027         this.fireEvent("panelactivated", this, panel);
33028     },
33029
33030     /**
33031      * Shows the specified panel.
33032      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
33033      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
33034      */
33035     showPanel : function(panel){
33036         if(panel = this.getPanel(panel)){
33037             if(this.tabs){
33038                 var tab = this.tabs.getTab(panel.getEl().id);
33039                 if(tab.isHidden()){
33040                     this.tabs.unhideTab(tab.id);
33041                 }
33042                 tab.activate();
33043             }else{
33044                 this.setActivePanel(panel);
33045             }
33046         }
33047         return panel;
33048     },
33049
33050     /**
33051      * Get the active panel for this region.
33052      * @return {Roo.ContentPanel} The active panel or null
33053      */
33054     getActivePanel : function(){
33055         return this.activePanel;
33056     },
33057
33058     validateVisibility : function(){
33059         if(this.panels.getCount() < 1){
33060             this.updateTitle("&#160;");
33061             this.closeBtn.hide();
33062             this.hide();
33063         }else{
33064             if(!this.isVisible()){
33065                 this.show();
33066             }
33067         }
33068     },
33069
33070     /**
33071      * Adds the passed ContentPanel(s) to this region.
33072      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33073      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
33074      */
33075     add : function(panel){
33076         if(arguments.length > 1){
33077             for(var i = 0, len = arguments.length; i < len; i++) {
33078                 this.add(arguments[i]);
33079             }
33080             return null;
33081         }
33082         if(this.hasPanel(panel)){
33083             this.showPanel(panel);
33084             return panel;
33085         }
33086         panel.setRegion(this);
33087         this.panels.add(panel);
33088         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
33089             this.bodyEl.dom.appendChild(panel.getEl().dom);
33090             if(panel.background !== true){
33091                 this.setActivePanel(panel);
33092             }
33093             this.fireEvent("paneladded", this, panel);
33094             return panel;
33095         }
33096         if(!this.tabs){
33097             this.initTabs();
33098         }else{
33099             this.initPanelAsTab(panel);
33100         }
33101         if(panel.background !== true){
33102             this.tabs.activate(panel.getEl().id);
33103         }
33104         this.fireEvent("paneladded", this, panel);
33105         return panel;
33106     },
33107
33108     /**
33109      * Hides the tab for the specified panel.
33110      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33111      */
33112     hidePanel : function(panel){
33113         if(this.tabs && (panel = this.getPanel(panel))){
33114             this.tabs.hideTab(panel.getEl().id);
33115         }
33116     },
33117
33118     /**
33119      * Unhides the tab for a previously hidden panel.
33120      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33121      */
33122     unhidePanel : function(panel){
33123         if(this.tabs && (panel = this.getPanel(panel))){
33124             this.tabs.unhideTab(panel.getEl().id);
33125         }
33126     },
33127
33128     clearPanels : function(){
33129         while(this.panels.getCount() > 0){
33130              this.remove(this.panels.first());
33131         }
33132     },
33133
33134     /**
33135      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33136      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33137      * @param {Boolean} preservePanel Overrides the config preservePanel option
33138      * @return {Roo.ContentPanel} The panel that was removed
33139      */
33140     remove : function(panel, preservePanel){
33141         panel = this.getPanel(panel);
33142         if(!panel){
33143             return null;
33144         }
33145         var e = {};
33146         this.fireEvent("beforeremove", this, panel, e);
33147         if(e.cancel === true){
33148             return null;
33149         }
33150         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
33151         var panelId = panel.getId();
33152         this.panels.removeKey(panelId);
33153         if(preservePanel){
33154             document.body.appendChild(panel.getEl().dom);
33155         }
33156         if(this.tabs){
33157             this.tabs.removeTab(panel.getEl().id);
33158         }else if (!preservePanel){
33159             this.bodyEl.dom.removeChild(panel.getEl().dom);
33160         }
33161         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
33162             var p = this.panels.first();
33163             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
33164             tempEl.appendChild(p.getEl().dom);
33165             this.bodyEl.update("");
33166             this.bodyEl.dom.appendChild(p.getEl().dom);
33167             tempEl = null;
33168             this.updateTitle(p.getTitle());
33169             this.tabs = null;
33170             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
33171             this.setActivePanel(p);
33172         }
33173         panel.setRegion(null);
33174         if(this.activePanel == panel){
33175             this.activePanel = null;
33176         }
33177         if(this.config.autoDestroy !== false && preservePanel !== true){
33178             try{panel.destroy();}catch(e){}
33179         }
33180         this.fireEvent("panelremoved", this, panel);
33181         return panel;
33182     },
33183
33184     /**
33185      * Returns the TabPanel component used by this region
33186      * @return {Roo.TabPanel}
33187      */
33188     getTabs : function(){
33189         return this.tabs;
33190     },
33191
33192     createTool : function(parentEl, className){
33193         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
33194             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
33195         btn.addClassOnOver("x-layout-tools-button-over");
33196         return btn;
33197     }
33198 });/*
33199  * Based on:
33200  * Ext JS Library 1.1.1
33201  * Copyright(c) 2006-2007, Ext JS, LLC.
33202  *
33203  * Originally Released Under LGPL - original licence link has changed is not relivant.
33204  *
33205  * Fork - LGPL
33206  * <script type="text/javascript">
33207  */
33208  
33209
33210
33211 /**
33212  * @class Roo.SplitLayoutRegion
33213  * @extends Roo.LayoutRegion
33214  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
33215  */
33216 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
33217     this.cursor = cursor;
33218     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
33219 };
33220
33221 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
33222     splitTip : "Drag to resize.",
33223     collapsibleSplitTip : "Drag to resize. Double click to hide.",
33224     useSplitTips : false,
33225
33226     applyConfig : function(config){
33227         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
33228         if(config.split){
33229             if(!this.split){
33230                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
33231                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
33232                 /** The SplitBar for this region 
33233                 * @type Roo.SplitBar */
33234                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
33235                 this.split.on("moved", this.onSplitMove, this);
33236                 this.split.useShim = config.useShim === true;
33237                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
33238                 if(this.useSplitTips){
33239                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
33240                 }
33241                 if(config.collapsible){
33242                     this.split.el.on("dblclick", this.collapse,  this);
33243                 }
33244             }
33245             if(typeof config.minSize != "undefined"){
33246                 this.split.minSize = config.minSize;
33247             }
33248             if(typeof config.maxSize != "undefined"){
33249                 this.split.maxSize = config.maxSize;
33250             }
33251             if(config.hideWhenEmpty || config.hidden || config.collapsed){
33252                 this.hideSplitter();
33253             }
33254         }
33255     },
33256
33257     getHMaxSize : function(){
33258          var cmax = this.config.maxSize || 10000;
33259          var center = this.mgr.getRegion("center");
33260          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
33261     },
33262
33263     getVMaxSize : function(){
33264          var cmax = this.config.maxSize || 10000;
33265          var center = this.mgr.getRegion("center");
33266          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
33267     },
33268
33269     onSplitMove : function(split, newSize){
33270         this.fireEvent("resized", this, newSize);
33271     },
33272     
33273     /** 
33274      * Returns the {@link Roo.SplitBar} for this region.
33275      * @return {Roo.SplitBar}
33276      */
33277     getSplitBar : function(){
33278         return this.split;
33279     },
33280     
33281     hide : function(){
33282         this.hideSplitter();
33283         Roo.SplitLayoutRegion.superclass.hide.call(this);
33284     },
33285
33286     hideSplitter : function(){
33287         if(this.split){
33288             this.split.el.setLocation(-2000,-2000);
33289             this.split.el.hide();
33290         }
33291     },
33292
33293     show : function(){
33294         if(this.split){
33295             this.split.el.show();
33296         }
33297         Roo.SplitLayoutRegion.superclass.show.call(this);
33298     },
33299     
33300     beforeSlide: function(){
33301         if(Roo.isGecko){// firefox overflow auto bug workaround
33302             this.bodyEl.clip();
33303             if(this.tabs) this.tabs.bodyEl.clip();
33304             if(this.activePanel){
33305                 this.activePanel.getEl().clip();
33306                 
33307                 if(this.activePanel.beforeSlide){
33308                     this.activePanel.beforeSlide();
33309                 }
33310             }
33311         }
33312     },
33313     
33314     afterSlide : function(){
33315         if(Roo.isGecko){// firefox overflow auto bug workaround
33316             this.bodyEl.unclip();
33317             if(this.tabs) this.tabs.bodyEl.unclip();
33318             if(this.activePanel){
33319                 this.activePanel.getEl().unclip();
33320                 if(this.activePanel.afterSlide){
33321                     this.activePanel.afterSlide();
33322                 }
33323             }
33324         }
33325     },
33326
33327     initAutoHide : function(){
33328         if(this.autoHide !== false){
33329             if(!this.autoHideHd){
33330                 var st = new Roo.util.DelayedTask(this.slideIn, this);
33331                 this.autoHideHd = {
33332                     "mouseout": function(e){
33333                         if(!e.within(this.el, true)){
33334                             st.delay(500);
33335                         }
33336                     },
33337                     "mouseover" : function(e){
33338                         st.cancel();
33339                     },
33340                     scope : this
33341                 };
33342             }
33343             this.el.on(this.autoHideHd);
33344         }
33345     },
33346
33347     clearAutoHide : function(){
33348         if(this.autoHide !== false){
33349             this.el.un("mouseout", this.autoHideHd.mouseout);
33350             this.el.un("mouseover", this.autoHideHd.mouseover);
33351         }
33352     },
33353
33354     clearMonitor : function(){
33355         Roo.get(document).un("click", this.slideInIf, this);
33356     },
33357
33358     // these names are backwards but not changed for compat
33359     slideOut : function(){
33360         if(this.isSlid || this.el.hasActiveFx()){
33361             return;
33362         }
33363         this.isSlid = true;
33364         if(this.collapseBtn){
33365             this.collapseBtn.hide();
33366         }
33367         this.closeBtnState = this.closeBtn.getStyle('display');
33368         this.closeBtn.hide();
33369         if(this.stickBtn){
33370             this.stickBtn.show();
33371         }
33372         this.el.show();
33373         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
33374         this.beforeSlide();
33375         this.el.setStyle("z-index", 10001);
33376         this.el.slideIn(this.getSlideAnchor(), {
33377             callback: function(){
33378                 this.afterSlide();
33379                 this.initAutoHide();
33380                 Roo.get(document).on("click", this.slideInIf, this);
33381                 this.fireEvent("slideshow", this);
33382             },
33383             scope: this,
33384             block: true
33385         });
33386     },
33387
33388     afterSlideIn : function(){
33389         this.clearAutoHide();
33390         this.isSlid = false;
33391         this.clearMonitor();
33392         this.el.setStyle("z-index", "");
33393         if(this.collapseBtn){
33394             this.collapseBtn.show();
33395         }
33396         this.closeBtn.setStyle('display', this.closeBtnState);
33397         if(this.stickBtn){
33398             this.stickBtn.hide();
33399         }
33400         this.fireEvent("slidehide", this);
33401     },
33402
33403     slideIn : function(cb){
33404         if(!this.isSlid || this.el.hasActiveFx()){
33405             Roo.callback(cb);
33406             return;
33407         }
33408         this.isSlid = false;
33409         this.beforeSlide();
33410         this.el.slideOut(this.getSlideAnchor(), {
33411             callback: function(){
33412                 this.el.setLeftTop(-10000, -10000);
33413                 this.afterSlide();
33414                 this.afterSlideIn();
33415                 Roo.callback(cb);
33416             },
33417             scope: this,
33418             block: true
33419         });
33420     },
33421     
33422     slideInIf : function(e){
33423         if(!e.within(this.el)){
33424             this.slideIn();
33425         }
33426     },
33427
33428     animateCollapse : function(){
33429         this.beforeSlide();
33430         this.el.setStyle("z-index", 20000);
33431         var anchor = this.getSlideAnchor();
33432         this.el.slideOut(anchor, {
33433             callback : function(){
33434                 this.el.setStyle("z-index", "");
33435                 this.collapsedEl.slideIn(anchor, {duration:.3});
33436                 this.afterSlide();
33437                 this.el.setLocation(-10000,-10000);
33438                 this.el.hide();
33439                 this.fireEvent("collapsed", this);
33440             },
33441             scope: this,
33442             block: true
33443         });
33444     },
33445
33446     animateExpand : function(){
33447         this.beforeSlide();
33448         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
33449         this.el.setStyle("z-index", 20000);
33450         this.collapsedEl.hide({
33451             duration:.1
33452         });
33453         this.el.slideIn(this.getSlideAnchor(), {
33454             callback : function(){
33455                 this.el.setStyle("z-index", "");
33456                 this.afterSlide();
33457                 if(this.split){
33458                     this.split.el.show();
33459                 }
33460                 this.fireEvent("invalidated", this);
33461                 this.fireEvent("expanded", this);
33462             },
33463             scope: this,
33464             block: true
33465         });
33466     },
33467
33468     anchors : {
33469         "west" : "left",
33470         "east" : "right",
33471         "north" : "top",
33472         "south" : "bottom"
33473     },
33474
33475     sanchors : {
33476         "west" : "l",
33477         "east" : "r",
33478         "north" : "t",
33479         "south" : "b"
33480     },
33481
33482     canchors : {
33483         "west" : "tl-tr",
33484         "east" : "tr-tl",
33485         "north" : "tl-bl",
33486         "south" : "bl-tl"
33487     },
33488
33489     getAnchor : function(){
33490         return this.anchors[this.position];
33491     },
33492
33493     getCollapseAnchor : function(){
33494         return this.canchors[this.position];
33495     },
33496
33497     getSlideAnchor : function(){
33498         return this.sanchors[this.position];
33499     },
33500
33501     getAlignAdj : function(){
33502         var cm = this.cmargins;
33503         switch(this.position){
33504             case "west":
33505                 return [0, 0];
33506             break;
33507             case "east":
33508                 return [0, 0];
33509             break;
33510             case "north":
33511                 return [0, 0];
33512             break;
33513             case "south":
33514                 return [0, 0];
33515             break;
33516         }
33517     },
33518
33519     getExpandAdj : function(){
33520         var c = this.collapsedEl, cm = this.cmargins;
33521         switch(this.position){
33522             case "west":
33523                 return [-(cm.right+c.getWidth()+cm.left), 0];
33524             break;
33525             case "east":
33526                 return [cm.right+c.getWidth()+cm.left, 0];
33527             break;
33528             case "north":
33529                 return [0, -(cm.top+cm.bottom+c.getHeight())];
33530             break;
33531             case "south":
33532                 return [0, cm.top+cm.bottom+c.getHeight()];
33533             break;
33534         }
33535     }
33536 });/*
33537  * Based on:
33538  * Ext JS Library 1.1.1
33539  * Copyright(c) 2006-2007, Ext JS, LLC.
33540  *
33541  * Originally Released Under LGPL - original licence link has changed is not relivant.
33542  *
33543  * Fork - LGPL
33544  * <script type="text/javascript">
33545  */
33546 /*
33547  * These classes are private internal classes
33548  */
33549 Roo.CenterLayoutRegion = function(mgr, config){
33550     Roo.LayoutRegion.call(this, mgr, config, "center");
33551     this.visible = true;
33552     this.minWidth = config.minWidth || 20;
33553     this.minHeight = config.minHeight || 20;
33554 };
33555
33556 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
33557     hide : function(){
33558         // center panel can't be hidden
33559     },
33560     
33561     show : function(){
33562         // center panel can't be hidden
33563     },
33564     
33565     getMinWidth: function(){
33566         return this.minWidth;
33567     },
33568     
33569     getMinHeight: function(){
33570         return this.minHeight;
33571     }
33572 });
33573
33574
33575 Roo.NorthLayoutRegion = function(mgr, config){
33576     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
33577     if(this.split){
33578         this.split.placement = Roo.SplitBar.TOP;
33579         this.split.orientation = Roo.SplitBar.VERTICAL;
33580         this.split.el.addClass("x-layout-split-v");
33581     }
33582     var size = config.initialSize || config.height;
33583     if(typeof size != "undefined"){
33584         this.el.setHeight(size);
33585     }
33586 };
33587 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
33588     orientation: Roo.SplitBar.VERTICAL,
33589     getBox : function(){
33590         if(this.collapsed){
33591             return this.collapsedEl.getBox();
33592         }
33593         var box = this.el.getBox();
33594         if(this.split){
33595             box.height += this.split.el.getHeight();
33596         }
33597         return box;
33598     },
33599     
33600     updateBox : function(box){
33601         if(this.split && !this.collapsed){
33602             box.height -= this.split.el.getHeight();
33603             this.split.el.setLeft(box.x);
33604             this.split.el.setTop(box.y+box.height);
33605             this.split.el.setWidth(box.width);
33606         }
33607         if(this.collapsed){
33608             this.updateBody(box.width, null);
33609         }
33610         Roo.LayoutRegion.prototype.updateBox.call(this, box);
33611     }
33612 });
33613
33614 Roo.SouthLayoutRegion = function(mgr, config){
33615     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
33616     if(this.split){
33617         this.split.placement = Roo.SplitBar.BOTTOM;
33618         this.split.orientation = Roo.SplitBar.VERTICAL;
33619         this.split.el.addClass("x-layout-split-v");
33620     }
33621     var size = config.initialSize || config.height;
33622     if(typeof size != "undefined"){
33623         this.el.setHeight(size);
33624     }
33625 };
33626 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
33627     orientation: Roo.SplitBar.VERTICAL,
33628     getBox : function(){
33629         if(this.collapsed){
33630             return this.collapsedEl.getBox();
33631         }
33632         var box = this.el.getBox();
33633         if(this.split){
33634             var sh = this.split.el.getHeight();
33635             box.height += sh;
33636             box.y -= sh;
33637         }
33638         return box;
33639     },
33640     
33641     updateBox : function(box){
33642         if(this.split && !this.collapsed){
33643             var sh = this.split.el.getHeight();
33644             box.height -= sh;
33645             box.y += sh;
33646             this.split.el.setLeft(box.x);
33647             this.split.el.setTop(box.y-sh);
33648             this.split.el.setWidth(box.width);
33649         }
33650         if(this.collapsed){
33651             this.updateBody(box.width, null);
33652         }
33653         Roo.LayoutRegion.prototype.updateBox.call(this, box);
33654     }
33655 });
33656
33657 Roo.EastLayoutRegion = function(mgr, config){
33658     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
33659     if(this.split){
33660         this.split.placement = Roo.SplitBar.RIGHT;
33661         this.split.orientation = Roo.SplitBar.HORIZONTAL;
33662         this.split.el.addClass("x-layout-split-h");
33663     }
33664     var size = config.initialSize || config.width;
33665     if(typeof size != "undefined"){
33666         this.el.setWidth(size);
33667     }
33668 };
33669 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
33670     orientation: Roo.SplitBar.HORIZONTAL,
33671     getBox : function(){
33672         if(this.collapsed){
33673             return this.collapsedEl.getBox();
33674         }
33675         var box = this.el.getBox();
33676         if(this.split){
33677             var sw = this.split.el.getWidth();
33678             box.width += sw;
33679             box.x -= sw;
33680         }
33681         return box;
33682     },
33683
33684     updateBox : function(box){
33685         if(this.split && !this.collapsed){
33686             var sw = this.split.el.getWidth();
33687             box.width -= sw;
33688             this.split.el.setLeft(box.x);
33689             this.split.el.setTop(box.y);
33690             this.split.el.setHeight(box.height);
33691             box.x += sw;
33692         }
33693         if(this.collapsed){
33694             this.updateBody(null, box.height);
33695         }
33696         Roo.LayoutRegion.prototype.updateBox.call(this, box);
33697     }
33698 });
33699
33700 Roo.WestLayoutRegion = function(mgr, config){
33701     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
33702     if(this.split){
33703         this.split.placement = Roo.SplitBar.LEFT;
33704         this.split.orientation = Roo.SplitBar.HORIZONTAL;
33705         this.split.el.addClass("x-layout-split-h");
33706     }
33707     var size = config.initialSize || config.width;
33708     if(typeof size != "undefined"){
33709         this.el.setWidth(size);
33710     }
33711 };
33712 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
33713     orientation: Roo.SplitBar.HORIZONTAL,
33714     getBox : function(){
33715         if(this.collapsed){
33716             return this.collapsedEl.getBox();
33717         }
33718         var box = this.el.getBox();
33719         if(this.split){
33720             box.width += this.split.el.getWidth();
33721         }
33722         return box;
33723     },
33724     
33725     updateBox : function(box){
33726         if(this.split && !this.collapsed){
33727             var sw = this.split.el.getWidth();
33728             box.width -= sw;
33729             this.split.el.setLeft(box.x+box.width);
33730             this.split.el.setTop(box.y);
33731             this.split.el.setHeight(box.height);
33732         }
33733         if(this.collapsed){
33734             this.updateBody(null, box.height);
33735         }
33736         Roo.LayoutRegion.prototype.updateBox.call(this, box);
33737     }
33738 });
33739 /*
33740  * Based on:
33741  * Ext JS Library 1.1.1
33742  * Copyright(c) 2006-2007, Ext JS, LLC.
33743  *
33744  * Originally Released Under LGPL - original licence link has changed is not relivant.
33745  *
33746  * Fork - LGPL
33747  * <script type="text/javascript">
33748  */
33749  
33750  
33751 /*
33752  * Private internal class for reading and applying state
33753  */
33754 Roo.LayoutStateManager = function(layout){
33755      // default empty state
33756      this.state = {
33757         north: {},
33758         south: {},
33759         east: {},
33760         west: {}       
33761     };
33762 };
33763
33764 Roo.LayoutStateManager.prototype = {
33765     init : function(layout, provider){
33766         this.provider = provider;
33767         var state = provider.get(layout.id+"-layout-state");
33768         if(state){
33769             var wasUpdating = layout.isUpdating();
33770             if(!wasUpdating){
33771                 layout.beginUpdate();
33772             }
33773             for(var key in state){
33774                 if(typeof state[key] != "function"){
33775                     var rstate = state[key];
33776                     var r = layout.getRegion(key);
33777                     if(r && rstate){
33778                         if(rstate.size){
33779                             r.resizeTo(rstate.size);
33780                         }
33781                         if(rstate.collapsed == true){
33782                             r.collapse(true);
33783                         }else{
33784                             r.expand(null, true);
33785                         }
33786                     }
33787                 }
33788             }
33789             if(!wasUpdating){
33790                 layout.endUpdate();
33791             }
33792             this.state = state; 
33793         }
33794         this.layout = layout;
33795         layout.on("regionresized", this.onRegionResized, this);
33796         layout.on("regioncollapsed", this.onRegionCollapsed, this);
33797         layout.on("regionexpanded", this.onRegionExpanded, this);
33798     },
33799     
33800     storeState : function(){
33801         this.provider.set(this.layout.id+"-layout-state", this.state);
33802     },
33803     
33804     onRegionResized : function(region, newSize){
33805         this.state[region.getPosition()].size = newSize;
33806         this.storeState();
33807     },
33808     
33809     onRegionCollapsed : function(region){
33810         this.state[region.getPosition()].collapsed = true;
33811         this.storeState();
33812     },
33813     
33814     onRegionExpanded : function(region){
33815         this.state[region.getPosition()].collapsed = false;
33816         this.storeState();
33817     }
33818 };/*
33819  * Based on:
33820  * Ext JS Library 1.1.1
33821  * Copyright(c) 2006-2007, Ext JS, LLC.
33822  *
33823  * Originally Released Under LGPL - original licence link has changed is not relivant.
33824  *
33825  * Fork - LGPL
33826  * <script type="text/javascript">
33827  */
33828 /**
33829  * @class Roo.ContentPanel
33830  * @extends Roo.util.Observable
33831  * A basic ContentPanel element.
33832  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
33833  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
33834  * @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
33835  * @cfg {Boolean}   closable      True if the panel can be closed/removed
33836  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
33837  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
33838  * @cfg {Toolbar}   toolbar       A toolbar for this panel
33839  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
33840  * @cfg {String} title          The title for this panel
33841  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
33842  * @cfg {String} url            Calls {@link #setUrl} with this value
33843  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
33844  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
33845  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
33846  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
33847
33848  * @constructor
33849  * Create a new ContentPanel.
33850  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
33851  * @param {String/Object} config A string to set only the title or a config object
33852  * @param {String} content (optional) Set the HTML content for this panel
33853  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
33854  */
33855 Roo.ContentPanel = function(el, config, content){
33856     
33857      
33858     /*
33859     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
33860         config = el;
33861         el = Roo.id();
33862     }
33863     if (config && config.parentLayout) { 
33864         el = config.parentLayout.el.createChild(); 
33865     }
33866     */
33867     if(el.autoCreate){ // xtype is available if this is called from factory
33868         config = el;
33869         el = Roo.id();
33870     }
33871     this.el = Roo.get(el);
33872     if(!this.el && config && config.autoCreate){
33873         if(typeof config.autoCreate == "object"){
33874             if(!config.autoCreate.id){
33875                 config.autoCreate.id = config.id||el;
33876             }
33877             this.el = Roo.DomHelper.append(document.body,
33878                         config.autoCreate, true);
33879         }else{
33880             this.el = Roo.DomHelper.append(document.body,
33881                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
33882         }
33883     }
33884     this.closable = false;
33885     this.loaded = false;
33886     this.active = false;
33887     if(typeof config == "string"){
33888         this.title = config;
33889     }else{
33890         Roo.apply(this, config);
33891     }
33892     
33893     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
33894         this.wrapEl = this.el.wrap();
33895         this.toolbar.container = this.el.insertSibling(false, 'before');
33896         this.toolbar = new Roo.Toolbar(this.toolbar);
33897     }
33898     
33899     // xtype created footer. - not sure if will work as we normally have to render first..
33900     if (this.footer && !this.footer.el && this.footer.xtype) {
33901         if (!this.wrapEl) {
33902             this.wrapEl = this.el.wrap();
33903         }
33904     
33905         this.footer.container = this.wrapEl.createChild();
33906          
33907         this.footer = Roo.factory(this.footer, Roo);
33908         
33909     }
33910     
33911     if(this.resizeEl){
33912         this.resizeEl = Roo.get(this.resizeEl, true);
33913     }else{
33914         this.resizeEl = this.el;
33915     }
33916     // handle view.xtype
33917     
33918  
33919     
33920     
33921     this.addEvents({
33922         /**
33923          * @event activate
33924          * Fires when this panel is activated. 
33925          * @param {Roo.ContentPanel} this
33926          */
33927         "activate" : true,
33928         /**
33929          * @event deactivate
33930          * Fires when this panel is activated. 
33931          * @param {Roo.ContentPanel} this
33932          */
33933         "deactivate" : true,
33934
33935         /**
33936          * @event resize
33937          * Fires when this panel is resized if fitToFrame is true.
33938          * @param {Roo.ContentPanel} this
33939          * @param {Number} width The width after any component adjustments
33940          * @param {Number} height The height after any component adjustments
33941          */
33942         "resize" : true,
33943         
33944          /**
33945          * @event render
33946          * Fires when this tab is created
33947          * @param {Roo.ContentPanel} this
33948          */
33949         "render" : true
33950         
33951         
33952         
33953     });
33954     
33955
33956     
33957     
33958     if(this.autoScroll){
33959         this.resizeEl.setStyle("overflow", "auto");
33960     } else {
33961         // fix randome scrolling
33962         this.el.on('scroll', function() {
33963             Roo.log('fix random scolling');
33964             this.scrollTo('top',0); 
33965         });
33966     }
33967     content = content || this.content;
33968     if(content){
33969         this.setContent(content);
33970     }
33971     if(config && config.url){
33972         this.setUrl(this.url, this.params, this.loadOnce);
33973     }
33974     
33975     
33976     
33977     Roo.ContentPanel.superclass.constructor.call(this);
33978     
33979     if (this.view && typeof(this.view.xtype) != 'undefined') {
33980         this.view.el = this.el.appendChild(document.createElement("div"));
33981         this.view = Roo.factory(this.view); 
33982         this.view.render  &&  this.view.render(false, '');  
33983     }
33984     
33985     
33986     this.fireEvent('render', this);
33987 };
33988
33989 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
33990     tabTip:'',
33991     setRegion : function(region){
33992         this.region = region;
33993         if(region){
33994            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
33995         }else{
33996            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
33997         } 
33998     },
33999     
34000     /**
34001      * Returns the toolbar for this Panel if one was configured. 
34002      * @return {Roo.Toolbar} 
34003      */
34004     getToolbar : function(){
34005         return this.toolbar;
34006     },
34007     
34008     setActiveState : function(active){
34009         this.active = active;
34010         if(!active){
34011             this.fireEvent("deactivate", this);
34012         }else{
34013             this.fireEvent("activate", this);
34014         }
34015     },
34016     /**
34017      * Updates this panel's element
34018      * @param {String} content The new content
34019      * @param {Boolean} loadScripts (optional) true to look for and process scripts
34020     */
34021     setContent : function(content, loadScripts){
34022         this.el.update(content, loadScripts);
34023     },
34024
34025     ignoreResize : function(w, h){
34026         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
34027             return true;
34028         }else{
34029             this.lastSize = {width: w, height: h};
34030             return false;
34031         }
34032     },
34033     /**
34034      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
34035      * @return {Roo.UpdateManager} The UpdateManager
34036      */
34037     getUpdateManager : function(){
34038         return this.el.getUpdateManager();
34039     },
34040      /**
34041      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
34042      * @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:
34043 <pre><code>
34044 panel.load({
34045     url: "your-url.php",
34046     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
34047     callback: yourFunction,
34048     scope: yourObject, //(optional scope)
34049     discardUrl: false,
34050     nocache: false,
34051     text: "Loading...",
34052     timeout: 30,
34053     scripts: false
34054 });
34055 </code></pre>
34056      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
34057      * 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.
34058      * @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}
34059      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
34060      * @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.
34061      * @return {Roo.ContentPanel} this
34062      */
34063     load : function(){
34064         var um = this.el.getUpdateManager();
34065         um.update.apply(um, arguments);
34066         return this;
34067     },
34068
34069
34070     /**
34071      * 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.
34072      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
34073      * @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)
34074      * @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)
34075      * @return {Roo.UpdateManager} The UpdateManager
34076      */
34077     setUrl : function(url, params, loadOnce){
34078         if(this.refreshDelegate){
34079             this.removeListener("activate", this.refreshDelegate);
34080         }
34081         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
34082         this.on("activate", this.refreshDelegate);
34083         return this.el.getUpdateManager();
34084     },
34085     
34086     _handleRefresh : function(url, params, loadOnce){
34087         if(!loadOnce || !this.loaded){
34088             var updater = this.el.getUpdateManager();
34089             updater.update(url, params, this._setLoaded.createDelegate(this));
34090         }
34091     },
34092     
34093     _setLoaded : function(){
34094         this.loaded = true;
34095     }, 
34096     
34097     /**
34098      * Returns this panel's id
34099      * @return {String} 
34100      */
34101     getId : function(){
34102         return this.el.id;
34103     },
34104     
34105     /** 
34106      * Returns this panel's element - used by regiosn to add.
34107      * @return {Roo.Element} 
34108      */
34109     getEl : function(){
34110         return this.wrapEl || this.el;
34111     },
34112     
34113     adjustForComponents : function(width, height)
34114     {
34115         //Roo.log('adjustForComponents ');
34116         if(this.resizeEl != this.el){
34117             width -= this.el.getFrameWidth('lr');
34118             height -= this.el.getFrameWidth('tb');
34119         }
34120         if(this.toolbar){
34121             var te = this.toolbar.getEl();
34122             height -= te.getHeight();
34123             te.setWidth(width);
34124         }
34125         if(this.footer){
34126             var te = this.footer.getEl();
34127             Roo.log("footer:" + te.getHeight());
34128             
34129             height -= te.getHeight();
34130             te.setWidth(width);
34131         }
34132         
34133         
34134         if(this.adjustments){
34135             width += this.adjustments[0];
34136             height += this.adjustments[1];
34137         }
34138         return {"width": width, "height": height};
34139     },
34140     
34141     setSize : function(width, height){
34142         if(this.fitToFrame && !this.ignoreResize(width, height)){
34143             if(this.fitContainer && this.resizeEl != this.el){
34144                 this.el.setSize(width, height);
34145             }
34146             var size = this.adjustForComponents(width, height);
34147             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
34148             this.fireEvent('resize', this, size.width, size.height);
34149         }
34150     },
34151     
34152     /**
34153      * Returns this panel's title
34154      * @return {String} 
34155      */
34156     getTitle : function(){
34157         return this.title;
34158     },
34159     
34160     /**
34161      * Set this panel's title
34162      * @param {String} title
34163      */
34164     setTitle : function(title){
34165         this.title = title;
34166         if(this.region){
34167             this.region.updatePanelTitle(this, title);
34168         }
34169     },
34170     
34171     /**
34172      * Returns true is this panel was configured to be closable
34173      * @return {Boolean} 
34174      */
34175     isClosable : function(){
34176         return this.closable;
34177     },
34178     
34179     beforeSlide : function(){
34180         this.el.clip();
34181         this.resizeEl.clip();
34182     },
34183     
34184     afterSlide : function(){
34185         this.el.unclip();
34186         this.resizeEl.unclip();
34187     },
34188     
34189     /**
34190      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
34191      *   Will fail silently if the {@link #setUrl} method has not been called.
34192      *   This does not activate the panel, just updates its content.
34193      */
34194     refresh : function(){
34195         if(this.refreshDelegate){
34196            this.loaded = false;
34197            this.refreshDelegate();
34198         }
34199     },
34200     
34201     /**
34202      * Destroys this panel
34203      */
34204     destroy : function(){
34205         this.el.removeAllListeners();
34206         var tempEl = document.createElement("span");
34207         tempEl.appendChild(this.el.dom);
34208         tempEl.innerHTML = "";
34209         this.el.remove();
34210         this.el = null;
34211     },
34212     
34213     /**
34214      * form - if the content panel contains a form - this is a reference to it.
34215      * @type {Roo.form.Form}
34216      */
34217     form : false,
34218     /**
34219      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
34220      *    This contains a reference to it.
34221      * @type {Roo.View}
34222      */
34223     view : false,
34224     
34225       /**
34226      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
34227      * <pre><code>
34228
34229 layout.addxtype({
34230        xtype : 'Form',
34231        items: [ .... ]
34232    }
34233 );
34234
34235 </code></pre>
34236      * @param {Object} cfg Xtype definition of item to add.
34237      */
34238     
34239     addxtype : function(cfg) {
34240         // add form..
34241         if (cfg.xtype.match(/^Form$/)) {
34242             
34243             var el;
34244             //if (this.footer) {
34245             //    el = this.footer.container.insertSibling(false, 'before');
34246             //} else {
34247                 el = this.el.createChild();
34248             //}
34249
34250             this.form = new  Roo.form.Form(cfg);
34251             
34252             
34253             if ( this.form.allItems.length) this.form.render(el.dom);
34254             return this.form;
34255         }
34256         // should only have one of theses..
34257         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
34258             // views.. should not be just added - used named prop 'view''
34259             
34260             cfg.el = this.el.appendChild(document.createElement("div"));
34261             // factory?
34262             
34263             var ret = new Roo.factory(cfg);
34264              
34265              ret.render && ret.render(false, ''); // render blank..
34266             this.view = ret;
34267             return ret;
34268         }
34269         return false;
34270     }
34271 });
34272
34273 /**
34274  * @class Roo.GridPanel
34275  * @extends Roo.ContentPanel
34276  * @constructor
34277  * Create a new GridPanel.
34278  * @param {Roo.grid.Grid} grid The grid for this panel
34279  * @param {String/Object} config A string to set only the panel's title, or a config object
34280  */
34281 Roo.GridPanel = function(grid, config){
34282     
34283   
34284     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
34285         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
34286         
34287     this.wrapper.dom.appendChild(grid.getGridEl().dom);
34288     
34289     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
34290     
34291     if(this.toolbar){
34292         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
34293     }
34294     // xtype created footer. - not sure if will work as we normally have to render first..
34295     if (this.footer && !this.footer.el && this.footer.xtype) {
34296         
34297         this.footer.container = this.grid.getView().getFooterPanel(true);
34298         this.footer.dataSource = this.grid.dataSource;
34299         this.footer = Roo.factory(this.footer, Roo);
34300         
34301     }
34302     
34303     grid.monitorWindowResize = false; // turn off autosizing
34304     grid.autoHeight = false;
34305     grid.autoWidth = false;
34306     this.grid = grid;
34307     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
34308 };
34309
34310 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
34311     getId : function(){
34312         return this.grid.id;
34313     },
34314     
34315     /**
34316      * Returns the grid for this panel
34317      * @return {Roo.grid.Grid} 
34318      */
34319     getGrid : function(){
34320         return this.grid;    
34321     },
34322     
34323     setSize : function(width, height){
34324         if(!this.ignoreResize(width, height)){
34325             var grid = this.grid;
34326             var size = this.adjustForComponents(width, height);
34327             grid.getGridEl().setSize(size.width, size.height);
34328             grid.autoSize();
34329         }
34330     },
34331     
34332     beforeSlide : function(){
34333         this.grid.getView().scroller.clip();
34334     },
34335     
34336     afterSlide : function(){
34337         this.grid.getView().scroller.unclip();
34338     },
34339     
34340     destroy : function(){
34341         this.grid.destroy();
34342         delete this.grid;
34343         Roo.GridPanel.superclass.destroy.call(this); 
34344     }
34345 });
34346
34347
34348 /**
34349  * @class Roo.NestedLayoutPanel
34350  * @extends Roo.ContentPanel
34351  * @constructor
34352  * Create a new NestedLayoutPanel.
34353  * 
34354  * 
34355  * @param {Roo.BorderLayout} layout The layout for this panel
34356  * @param {String/Object} config A string to set only the title or a config object
34357  */
34358 Roo.NestedLayoutPanel = function(layout, config)
34359 {
34360     // construct with only one argument..
34361     /* FIXME - implement nicer consturctors
34362     if (layout.layout) {
34363         config = layout;
34364         layout = config.layout;
34365         delete config.layout;
34366     }
34367     if (layout.xtype && !layout.getEl) {
34368         // then layout needs constructing..
34369         layout = Roo.factory(layout, Roo);
34370     }
34371     */
34372     
34373     
34374     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
34375     
34376     layout.monitorWindowResize = false; // turn off autosizing
34377     this.layout = layout;
34378     this.layout.getEl().addClass("x-layout-nested-layout");
34379     
34380     
34381     
34382     
34383 };
34384
34385 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
34386
34387     setSize : function(width, height){
34388         if(!this.ignoreResize(width, height)){
34389             var size = this.adjustForComponents(width, height);
34390             var el = this.layout.getEl();
34391             el.setSize(size.width, size.height);
34392             var touch = el.dom.offsetWidth;
34393             this.layout.layout();
34394             // ie requires a double layout on the first pass
34395             if(Roo.isIE && !this.initialized){
34396                 this.initialized = true;
34397                 this.layout.layout();
34398             }
34399         }
34400     },
34401     
34402     // activate all subpanels if not currently active..
34403     
34404     setActiveState : function(active){
34405         this.active = active;
34406         if(!active){
34407             this.fireEvent("deactivate", this);
34408             return;
34409         }
34410         
34411         this.fireEvent("activate", this);
34412         // not sure if this should happen before or after..
34413         if (!this.layout) {
34414             return; // should not happen..
34415         }
34416         var reg = false;
34417         for (var r in this.layout.regions) {
34418             reg = this.layout.getRegion(r);
34419             if (reg.getActivePanel()) {
34420                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
34421                 reg.setActivePanel(reg.getActivePanel());
34422                 continue;
34423             }
34424             if (!reg.panels.length) {
34425                 continue;
34426             }
34427             reg.showPanel(reg.getPanel(0));
34428         }
34429         
34430         
34431         
34432         
34433     },
34434     
34435     /**
34436      * Returns the nested BorderLayout for this panel
34437      * @return {Roo.BorderLayout} 
34438      */
34439     getLayout : function(){
34440         return this.layout;
34441     },
34442     
34443      /**
34444      * Adds a xtype elements to the layout of the nested panel
34445      * <pre><code>
34446
34447 panel.addxtype({
34448        xtype : 'ContentPanel',
34449        region: 'west',
34450        items: [ .... ]
34451    }
34452 );
34453
34454 panel.addxtype({
34455         xtype : 'NestedLayoutPanel',
34456         region: 'west',
34457         layout: {
34458            center: { },
34459            west: { }   
34460         },
34461         items : [ ... list of content panels or nested layout panels.. ]
34462    }
34463 );
34464 </code></pre>
34465      * @param {Object} cfg Xtype definition of item to add.
34466      */
34467     addxtype : function(cfg) {
34468         return this.layout.addxtype(cfg);
34469     
34470     }
34471 });
34472
34473 Roo.ScrollPanel = function(el, config, content){
34474     config = config || {};
34475     config.fitToFrame = true;
34476     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
34477     
34478     this.el.dom.style.overflow = "hidden";
34479     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
34480     this.el.removeClass("x-layout-inactive-content");
34481     this.el.on("mousewheel", this.onWheel, this);
34482
34483     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
34484     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
34485     up.unselectable(); down.unselectable();
34486     up.on("click", this.scrollUp, this);
34487     down.on("click", this.scrollDown, this);
34488     up.addClassOnOver("x-scroller-btn-over");
34489     down.addClassOnOver("x-scroller-btn-over");
34490     up.addClassOnClick("x-scroller-btn-click");
34491     down.addClassOnClick("x-scroller-btn-click");
34492     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
34493
34494     this.resizeEl = this.el;
34495     this.el = wrap; this.up = up; this.down = down;
34496 };
34497
34498 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
34499     increment : 100,
34500     wheelIncrement : 5,
34501     scrollUp : function(){
34502         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
34503     },
34504
34505     scrollDown : function(){
34506         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
34507     },
34508
34509     afterScroll : function(){
34510         var el = this.resizeEl;
34511         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
34512         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
34513         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
34514     },
34515
34516     setSize : function(){
34517         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
34518         this.afterScroll();
34519     },
34520
34521     onWheel : function(e){
34522         var d = e.getWheelDelta();
34523         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
34524         this.afterScroll();
34525         e.stopEvent();
34526     },
34527
34528     setContent : function(content, loadScripts){
34529         this.resizeEl.update(content, loadScripts);
34530     }
34531
34532 });
34533
34534
34535
34536
34537
34538
34539
34540
34541
34542 /**
34543  * @class Roo.TreePanel
34544  * @extends Roo.ContentPanel
34545  * @constructor
34546  * Create a new TreePanel. - defaults to fit/scoll contents.
34547  * @param {String/Object} config A string to set only the panel's title, or a config object
34548  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
34549  */
34550 Roo.TreePanel = function(config){
34551     var el = config.el;
34552     var tree = config.tree;
34553     delete config.tree; 
34554     delete config.el; // hopefull!
34555     
34556     // wrapper for IE7 strict & safari scroll issue
34557     
34558     var treeEl = el.createChild();
34559     config.resizeEl = treeEl;
34560     
34561     
34562     
34563     Roo.TreePanel.superclass.constructor.call(this, el, config);
34564  
34565  
34566     this.tree = new Roo.tree.TreePanel(treeEl , tree);
34567     //console.log(tree);
34568     this.on('activate', function()
34569     {
34570         if (this.tree.rendered) {
34571             return;
34572         }
34573         //console.log('render tree');
34574         this.tree.render();
34575     });
34576     // this should not be needed.. - it's actually the 'el' that resizes?
34577     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
34578     
34579     //this.on('resize',  function (cp, w, h) {
34580     //        this.tree.innerCt.setWidth(w);
34581     //        this.tree.innerCt.setHeight(h);
34582     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
34583     //});
34584
34585         
34586     
34587 };
34588
34589 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
34590     fitToFrame : true,
34591     autoScroll : true
34592 });
34593
34594
34595
34596
34597
34598
34599
34600
34601
34602
34603
34604 /*
34605  * Based on:
34606  * Ext JS Library 1.1.1
34607  * Copyright(c) 2006-2007, Ext JS, LLC.
34608  *
34609  * Originally Released Under LGPL - original licence link has changed is not relivant.
34610  *
34611  * Fork - LGPL
34612  * <script type="text/javascript">
34613  */
34614  
34615
34616 /**
34617  * @class Roo.ReaderLayout
34618  * @extends Roo.BorderLayout
34619  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
34620  * center region containing two nested regions (a top one for a list view and one for item preview below),
34621  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
34622  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
34623  * expedites the setup of the overall layout and regions for this common application style.
34624  * Example:
34625  <pre><code>
34626 var reader = new Roo.ReaderLayout();
34627 var CP = Roo.ContentPanel;  // shortcut for adding
34628
34629 reader.beginUpdate();
34630 reader.add("north", new CP("north", "North"));
34631 reader.add("west", new CP("west", {title: "West"}));
34632 reader.add("east", new CP("east", {title: "East"}));
34633
34634 reader.regions.listView.add(new CP("listView", "List"));
34635 reader.regions.preview.add(new CP("preview", "Preview"));
34636 reader.endUpdate();
34637 </code></pre>
34638 * @constructor
34639 * Create a new ReaderLayout
34640 * @param {Object} config Configuration options
34641 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
34642 * document.body if omitted)
34643 */
34644 Roo.ReaderLayout = function(config, renderTo){
34645     var c = config || {size:{}};
34646     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
34647         north: c.north !== false ? Roo.apply({
34648             split:false,
34649             initialSize: 32,
34650             titlebar: false
34651         }, c.north) : false,
34652         west: c.west !== false ? Roo.apply({
34653             split:true,
34654             initialSize: 200,
34655             minSize: 175,
34656             maxSize: 400,
34657             titlebar: true,
34658             collapsible: true,
34659             animate: true,
34660             margins:{left:5,right:0,bottom:5,top:5},
34661             cmargins:{left:5,right:5,bottom:5,top:5}
34662         }, c.west) : false,
34663         east: c.east !== false ? Roo.apply({
34664             split:true,
34665             initialSize: 200,
34666             minSize: 175,
34667             maxSize: 400,
34668             titlebar: true,
34669             collapsible: true,
34670             animate: true,
34671             margins:{left:0,right:5,bottom:5,top:5},
34672             cmargins:{left:5,right:5,bottom:5,top:5}
34673         }, c.east) : false,
34674         center: Roo.apply({
34675             tabPosition: 'top',
34676             autoScroll:false,
34677             closeOnTab: true,
34678             titlebar:false,
34679             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
34680         }, c.center)
34681     });
34682
34683     this.el.addClass('x-reader');
34684
34685     this.beginUpdate();
34686
34687     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
34688         south: c.preview !== false ? Roo.apply({
34689             split:true,
34690             initialSize: 200,
34691             minSize: 100,
34692             autoScroll:true,
34693             collapsible:true,
34694             titlebar: true,
34695             cmargins:{top:5,left:0, right:0, bottom:0}
34696         }, c.preview) : false,
34697         center: Roo.apply({
34698             autoScroll:false,
34699             titlebar:false,
34700             minHeight:200
34701         }, c.listView)
34702     });
34703     this.add('center', new Roo.NestedLayoutPanel(inner,
34704             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
34705
34706     this.endUpdate();
34707
34708     this.regions.preview = inner.getRegion('south');
34709     this.regions.listView = inner.getRegion('center');
34710 };
34711
34712 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
34713  * Based on:
34714  * Ext JS Library 1.1.1
34715  * Copyright(c) 2006-2007, Ext JS, LLC.
34716  *
34717  * Originally Released Under LGPL - original licence link has changed is not relivant.
34718  *
34719  * Fork - LGPL
34720  * <script type="text/javascript">
34721  */
34722  
34723 /**
34724  * @class Roo.grid.Grid
34725  * @extends Roo.util.Observable
34726  * This class represents the primary interface of a component based grid control.
34727  * <br><br>Usage:<pre><code>
34728  var grid = new Roo.grid.Grid("my-container-id", {
34729      ds: myDataStore,
34730      cm: myColModel,
34731      selModel: mySelectionModel,
34732      autoSizeColumns: true,
34733      monitorWindowResize: false,
34734      trackMouseOver: true
34735  });
34736  // set any options
34737  grid.render();
34738  * </code></pre>
34739  * <b>Common Problems:</b><br/>
34740  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
34741  * element will correct this<br/>
34742  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
34743  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
34744  * are unpredictable.<br/>
34745  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
34746  * grid to calculate dimensions/offsets.<br/>
34747   * @constructor
34748  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
34749  * The container MUST have some type of size defined for the grid to fill. The container will be
34750  * automatically set to position relative if it isn't already.
34751  * @param {Object} config A config object that sets properties on this grid.
34752  */
34753 Roo.grid.Grid = function(container, config){
34754         // initialize the container
34755         this.container = Roo.get(container);
34756         this.container.update("");
34757         this.container.setStyle("overflow", "hidden");
34758     this.container.addClass('x-grid-container');
34759
34760     this.id = this.container.id;
34761
34762     Roo.apply(this, config);
34763     // check and correct shorthanded configs
34764     if(this.ds){
34765         this.dataSource = this.ds;
34766         delete this.ds;
34767     }
34768     if(this.cm){
34769         this.colModel = this.cm;
34770         delete this.cm;
34771     }
34772     if(this.sm){
34773         this.selModel = this.sm;
34774         delete this.sm;
34775     }
34776
34777     if (this.selModel) {
34778         this.selModel = Roo.factory(this.selModel, Roo.grid);
34779         this.sm = this.selModel;
34780         this.sm.xmodule = this.xmodule || false;
34781     }
34782     if (typeof(this.colModel.config) == 'undefined') {
34783         this.colModel = new Roo.grid.ColumnModel(this.colModel);
34784         this.cm = this.colModel;
34785         this.cm.xmodule = this.xmodule || false;
34786     }
34787     if (this.dataSource) {
34788         this.dataSource= Roo.factory(this.dataSource, Roo.data);
34789         this.ds = this.dataSource;
34790         this.ds.xmodule = this.xmodule || false;
34791          
34792     }
34793     
34794     
34795     
34796     if(this.width){
34797         this.container.setWidth(this.width);
34798     }
34799
34800     if(this.height){
34801         this.container.setHeight(this.height);
34802     }
34803     /** @private */
34804         this.addEvents({
34805         // raw events
34806         /**
34807          * @event click
34808          * The raw click event for the entire grid.
34809          * @param {Roo.EventObject} e
34810          */
34811         "click" : true,
34812         /**
34813          * @event dblclick
34814          * The raw dblclick event for the entire grid.
34815          * @param {Roo.EventObject} e
34816          */
34817         "dblclick" : true,
34818         /**
34819          * @event contextmenu
34820          * The raw contextmenu event for the entire grid.
34821          * @param {Roo.EventObject} e
34822          */
34823         "contextmenu" : true,
34824         /**
34825          * @event mousedown
34826          * The raw mousedown event for the entire grid.
34827          * @param {Roo.EventObject} e
34828          */
34829         "mousedown" : true,
34830         /**
34831          * @event mouseup
34832          * The raw mouseup event for the entire grid.
34833          * @param {Roo.EventObject} e
34834          */
34835         "mouseup" : true,
34836         /**
34837          * @event mouseover
34838          * The raw mouseover event for the entire grid.
34839          * @param {Roo.EventObject} e
34840          */
34841         "mouseover" : true,
34842         /**
34843          * @event mouseout
34844          * The raw mouseout event for the entire grid.
34845          * @param {Roo.EventObject} e
34846          */
34847         "mouseout" : true,
34848         /**
34849          * @event keypress
34850          * The raw keypress event for the entire grid.
34851          * @param {Roo.EventObject} e
34852          */
34853         "keypress" : true,
34854         /**
34855          * @event keydown
34856          * The raw keydown event for the entire grid.
34857          * @param {Roo.EventObject} e
34858          */
34859         "keydown" : true,
34860
34861         // custom events
34862
34863         /**
34864          * @event cellclick
34865          * Fires when a cell is clicked
34866          * @param {Grid} this
34867          * @param {Number} rowIndex
34868          * @param {Number} columnIndex
34869          * @param {Roo.EventObject} e
34870          */
34871         "cellclick" : true,
34872         /**
34873          * @event celldblclick
34874          * Fires when a cell is double clicked
34875          * @param {Grid} this
34876          * @param {Number} rowIndex
34877          * @param {Number} columnIndex
34878          * @param {Roo.EventObject} e
34879          */
34880         "celldblclick" : true,
34881         /**
34882          * @event rowclick
34883          * Fires when a row is clicked
34884          * @param {Grid} this
34885          * @param {Number} rowIndex
34886          * @param {Roo.EventObject} e
34887          */
34888         "rowclick" : true,
34889         /**
34890          * @event rowdblclick
34891          * Fires when a row is double clicked
34892          * @param {Grid} this
34893          * @param {Number} rowIndex
34894          * @param {Roo.EventObject} e
34895          */
34896         "rowdblclick" : true,
34897         /**
34898          * @event headerclick
34899          * Fires when a header is clicked
34900          * @param {Grid} this
34901          * @param {Number} columnIndex
34902          * @param {Roo.EventObject} e
34903          */
34904         "headerclick" : true,
34905         /**
34906          * @event headerdblclick
34907          * Fires when a header cell is double clicked
34908          * @param {Grid} this
34909          * @param {Number} columnIndex
34910          * @param {Roo.EventObject} e
34911          */
34912         "headerdblclick" : true,
34913         /**
34914          * @event rowcontextmenu
34915          * Fires when a row is right clicked
34916          * @param {Grid} this
34917          * @param {Number} rowIndex
34918          * @param {Roo.EventObject} e
34919          */
34920         "rowcontextmenu" : true,
34921         /**
34922          * @event cellcontextmenu
34923          * Fires when a cell is right clicked
34924          * @param {Grid} this
34925          * @param {Number} rowIndex
34926          * @param {Number} cellIndex
34927          * @param {Roo.EventObject} e
34928          */
34929          "cellcontextmenu" : true,
34930         /**
34931          * @event headercontextmenu
34932          * Fires when a header is right clicked
34933          * @param {Grid} this
34934          * @param {Number} columnIndex
34935          * @param {Roo.EventObject} e
34936          */
34937         "headercontextmenu" : true,
34938         /**
34939          * @event bodyscroll
34940          * Fires when the body element is scrolled
34941          * @param {Number} scrollLeft
34942          * @param {Number} scrollTop
34943          */
34944         "bodyscroll" : true,
34945         /**
34946          * @event columnresize
34947          * Fires when the user resizes a column
34948          * @param {Number} columnIndex
34949          * @param {Number} newSize
34950          */
34951         "columnresize" : true,
34952         /**
34953          * @event columnmove
34954          * Fires when the user moves a column
34955          * @param {Number} oldIndex
34956          * @param {Number} newIndex
34957          */
34958         "columnmove" : true,
34959         /**
34960          * @event startdrag
34961          * Fires when row(s) start being dragged
34962          * @param {Grid} this
34963          * @param {Roo.GridDD} dd The drag drop object
34964          * @param {event} e The raw browser event
34965          */
34966         "startdrag" : true,
34967         /**
34968          * @event enddrag
34969          * Fires when a drag operation is complete
34970          * @param {Grid} this
34971          * @param {Roo.GridDD} dd The drag drop object
34972          * @param {event} e The raw browser event
34973          */
34974         "enddrag" : true,
34975         /**
34976          * @event dragdrop
34977          * Fires when dragged row(s) are dropped on a valid DD target
34978          * @param {Grid} this
34979          * @param {Roo.GridDD} dd The drag drop object
34980          * @param {String} targetId The target drag drop object
34981          * @param {event} e The raw browser event
34982          */
34983         "dragdrop" : true,
34984         /**
34985          * @event dragover
34986          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
34987          * @param {Grid} this
34988          * @param {Roo.GridDD} dd The drag drop object
34989          * @param {String} targetId The target drag drop object
34990          * @param {event} e The raw browser event
34991          */
34992         "dragover" : true,
34993         /**
34994          * @event dragenter
34995          *  Fires when the dragged row(s) first cross another DD target while being dragged
34996          * @param {Grid} this
34997          * @param {Roo.GridDD} dd The drag drop object
34998          * @param {String} targetId The target drag drop object
34999          * @param {event} e The raw browser event
35000          */
35001         "dragenter" : true,
35002         /**
35003          * @event dragout
35004          * Fires when the dragged row(s) leave another DD target while being dragged
35005          * @param {Grid} this
35006          * @param {Roo.GridDD} dd The drag drop object
35007          * @param {String} targetId The target drag drop object
35008          * @param {event} e The raw browser event
35009          */
35010         "dragout" : true,
35011         /**
35012          * @event rowclass
35013          * Fires when a row is rendered, so you can change add a style to it.
35014          * @param {GridView} gridview   The grid view
35015          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
35016          */
35017         'rowclass' : true,
35018
35019         /**
35020          * @event render
35021          * Fires when the grid is rendered
35022          * @param {Grid} grid
35023          */
35024         'render' : true
35025     });
35026
35027     Roo.grid.Grid.superclass.constructor.call(this);
35028 };
35029 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
35030     
35031     /**
35032      * @cfg {String} ddGroup - drag drop group.
35033      */
35034
35035     /**
35036      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
35037      */
35038     minColumnWidth : 25,
35039
35040     /**
35041      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
35042      * <b>on initial render.</b> It is more efficient to explicitly size the columns
35043      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
35044      */
35045     autoSizeColumns : false,
35046
35047     /**
35048      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
35049      */
35050     autoSizeHeaders : true,
35051
35052     /**
35053      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
35054      */
35055     monitorWindowResize : true,
35056
35057     /**
35058      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
35059      * rows measured to get a columns size. Default is 0 (all rows).
35060      */
35061     maxRowsToMeasure : 0,
35062
35063     /**
35064      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
35065      */
35066     trackMouseOver : true,
35067
35068     /**
35069     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
35070     */
35071     
35072     /**
35073     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
35074     */
35075     enableDragDrop : false,
35076     
35077     /**
35078     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
35079     */
35080     enableColumnMove : true,
35081     
35082     /**
35083     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
35084     */
35085     enableColumnHide : true,
35086     
35087     /**
35088     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
35089     */
35090     enableRowHeightSync : false,
35091     
35092     /**
35093     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
35094     */
35095     stripeRows : true,
35096     
35097     /**
35098     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
35099     */
35100     autoHeight : false,
35101
35102     /**
35103      * @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.
35104      */
35105     autoExpandColumn : false,
35106
35107     /**
35108     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
35109     * Default is 50.
35110     */
35111     autoExpandMin : 50,
35112
35113     /**
35114     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
35115     */
35116     autoExpandMax : 1000,
35117
35118     /**
35119     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
35120     */
35121     view : null,
35122
35123     /**
35124     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
35125     */
35126     loadMask : false,
35127     /**
35128     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
35129     */
35130     dropTarget: false,
35131     
35132    
35133     
35134     // private
35135     rendered : false,
35136
35137     /**
35138     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
35139     * of a fixed width. Default is false.
35140     */
35141     /**
35142     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
35143     */
35144     /**
35145      * Called once after all setup has been completed and the grid is ready to be rendered.
35146      * @return {Roo.grid.Grid} this
35147      */
35148     render : function()
35149     {
35150         var c = this.container;
35151         // try to detect autoHeight/width mode
35152         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
35153             this.autoHeight = true;
35154         }
35155         var view = this.getView();
35156         view.init(this);
35157
35158         c.on("click", this.onClick, this);
35159         c.on("dblclick", this.onDblClick, this);
35160         c.on("contextmenu", this.onContextMenu, this);
35161         c.on("keydown", this.onKeyDown, this);
35162
35163         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
35164
35165         this.getSelectionModel().init(this);
35166
35167         view.render();
35168
35169         if(this.loadMask){
35170             this.loadMask = new Roo.LoadMask(this.container,
35171                     Roo.apply({store:this.dataSource}, this.loadMask));
35172         }
35173         
35174         
35175         if (this.toolbar && this.toolbar.xtype) {
35176             this.toolbar.container = this.getView().getHeaderPanel(true);
35177             this.toolbar = new Roo.Toolbar(this.toolbar);
35178         }
35179         if (this.footer && this.footer.xtype) {
35180             this.footer.dataSource = this.getDataSource();
35181             this.footer.container = this.getView().getFooterPanel(true);
35182             this.footer = Roo.factory(this.footer, Roo);
35183         }
35184         if (this.dropTarget && this.dropTarget.xtype) {
35185             delete this.dropTarget.xtype;
35186             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
35187         }
35188         
35189         
35190         this.rendered = true;
35191         this.fireEvent('render', this);
35192         return this;
35193     },
35194
35195         /**
35196          * Reconfigures the grid to use a different Store and Column Model.
35197          * The View will be bound to the new objects and refreshed.
35198          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
35199          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
35200          */
35201     reconfigure : function(dataSource, colModel){
35202         if(this.loadMask){
35203             this.loadMask.destroy();
35204             this.loadMask = new Roo.LoadMask(this.container,
35205                     Roo.apply({store:dataSource}, this.loadMask));
35206         }
35207         this.view.bind(dataSource, colModel);
35208         this.dataSource = dataSource;
35209         this.colModel = colModel;
35210         this.view.refresh(true);
35211     },
35212
35213     // private
35214     onKeyDown : function(e){
35215         this.fireEvent("keydown", e);
35216     },
35217
35218     /**
35219      * Destroy this grid.
35220      * @param {Boolean} removeEl True to remove the element
35221      */
35222     destroy : function(removeEl, keepListeners){
35223         if(this.loadMask){
35224             this.loadMask.destroy();
35225         }
35226         var c = this.container;
35227         c.removeAllListeners();
35228         this.view.destroy();
35229         this.colModel.purgeListeners();
35230         if(!keepListeners){
35231             this.purgeListeners();
35232         }
35233         c.update("");
35234         if(removeEl === true){
35235             c.remove();
35236         }
35237     },
35238
35239     // private
35240     processEvent : function(name, e){
35241         this.fireEvent(name, e);
35242         var t = e.getTarget();
35243         var v = this.view;
35244         var header = v.findHeaderIndex(t);
35245         if(header !== false){
35246             this.fireEvent("header" + name, this, header, e);
35247         }else{
35248             var row = v.findRowIndex(t);
35249             var cell = v.findCellIndex(t);
35250             if(row !== false){
35251                 this.fireEvent("row" + name, this, row, e);
35252                 if(cell !== false){
35253                     this.fireEvent("cell" + name, this, row, cell, e);
35254                 }
35255             }
35256         }
35257     },
35258
35259     // private
35260     onClick : function(e){
35261         this.processEvent("click", e);
35262     },
35263
35264     // private
35265     onContextMenu : function(e, t){
35266         this.processEvent("contextmenu", e);
35267     },
35268
35269     // private
35270     onDblClick : function(e){
35271         this.processEvent("dblclick", e);
35272     },
35273
35274     // private
35275     walkCells : function(row, col, step, fn, scope){
35276         var cm = this.colModel, clen = cm.getColumnCount();
35277         var ds = this.dataSource, rlen = ds.getCount(), first = true;
35278         if(step < 0){
35279             if(col < 0){
35280                 row--;
35281                 first = false;
35282             }
35283             while(row >= 0){
35284                 if(!first){
35285                     col = clen-1;
35286                 }
35287                 first = false;
35288                 while(col >= 0){
35289                     if(fn.call(scope || this, row, col, cm) === true){
35290                         return [row, col];
35291                     }
35292                     col--;
35293                 }
35294                 row--;
35295             }
35296         } else {
35297             if(col >= clen){
35298                 row++;
35299                 first = false;
35300             }
35301             while(row < rlen){
35302                 if(!first){
35303                     col = 0;
35304                 }
35305                 first = false;
35306                 while(col < clen){
35307                     if(fn.call(scope || this, row, col, cm) === true){
35308                         return [row, col];
35309                     }
35310                     col++;
35311                 }
35312                 row++;
35313             }
35314         }
35315         return null;
35316     },
35317
35318     // private
35319     getSelections : function(){
35320         return this.selModel.getSelections();
35321     },
35322
35323     /**
35324      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
35325      * but if manual update is required this method will initiate it.
35326      */
35327     autoSize : function(){
35328         if(this.rendered){
35329             this.view.layout();
35330             if(this.view.adjustForScroll){
35331                 this.view.adjustForScroll();
35332             }
35333         }
35334     },
35335
35336     /**
35337      * Returns the grid's underlying element.
35338      * @return {Element} The element
35339      */
35340     getGridEl : function(){
35341         return this.container;
35342     },
35343
35344     // private for compatibility, overridden by editor grid
35345     stopEditing : function(){},
35346
35347     /**
35348      * Returns the grid's SelectionModel.
35349      * @return {SelectionModel}
35350      */
35351     getSelectionModel : function(){
35352         if(!this.selModel){
35353             this.selModel = new Roo.grid.RowSelectionModel();
35354         }
35355         return this.selModel;
35356     },
35357
35358     /**
35359      * Returns the grid's DataSource.
35360      * @return {DataSource}
35361      */
35362     getDataSource : function(){
35363         return this.dataSource;
35364     },
35365
35366     /**
35367      * Returns the grid's ColumnModel.
35368      * @return {ColumnModel}
35369      */
35370     getColumnModel : function(){
35371         return this.colModel;
35372     },
35373
35374     /**
35375      * Returns the grid's GridView object.
35376      * @return {GridView}
35377      */
35378     getView : function(){
35379         if(!this.view){
35380             this.view = new Roo.grid.GridView(this.viewConfig);
35381         }
35382         return this.view;
35383     },
35384     /**
35385      * Called to get grid's drag proxy text, by default returns this.ddText.
35386      * @return {String}
35387      */
35388     getDragDropText : function(){
35389         var count = this.selModel.getCount();
35390         return String.format(this.ddText, count, count == 1 ? '' : 's');
35391     }
35392 });
35393 /**
35394  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
35395  * %0 is replaced with the number of selected rows.
35396  * @type String
35397  */
35398 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
35399  * Based on:
35400  * Ext JS Library 1.1.1
35401  * Copyright(c) 2006-2007, Ext JS, LLC.
35402  *
35403  * Originally Released Under LGPL - original licence link has changed is not relivant.
35404  *
35405  * Fork - LGPL
35406  * <script type="text/javascript">
35407  */
35408  
35409 Roo.grid.AbstractGridView = function(){
35410         this.grid = null;
35411         
35412         this.events = {
35413             "beforerowremoved" : true,
35414             "beforerowsinserted" : true,
35415             "beforerefresh" : true,
35416             "rowremoved" : true,
35417             "rowsinserted" : true,
35418             "rowupdated" : true,
35419             "refresh" : true
35420         };
35421     Roo.grid.AbstractGridView.superclass.constructor.call(this);
35422 };
35423
35424 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
35425     rowClass : "x-grid-row",
35426     cellClass : "x-grid-cell",
35427     tdClass : "x-grid-td",
35428     hdClass : "x-grid-hd",
35429     splitClass : "x-grid-hd-split",
35430     
35431         init: function(grid){
35432         this.grid = grid;
35433                 var cid = this.grid.getGridEl().id;
35434         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
35435         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
35436         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
35437         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
35438         },
35439         
35440         getColumnRenderers : function(){
35441         var renderers = [];
35442         var cm = this.grid.colModel;
35443         var colCount = cm.getColumnCount();
35444         for(var i = 0; i < colCount; i++){
35445             renderers[i] = cm.getRenderer(i);
35446         }
35447         return renderers;
35448     },
35449     
35450     getColumnIds : function(){
35451         var ids = [];
35452         var cm = this.grid.colModel;
35453         var colCount = cm.getColumnCount();
35454         for(var i = 0; i < colCount; i++){
35455             ids[i] = cm.getColumnId(i);
35456         }
35457         return ids;
35458     },
35459     
35460     getDataIndexes : function(){
35461         if(!this.indexMap){
35462             this.indexMap = this.buildIndexMap();
35463         }
35464         return this.indexMap.colToData;
35465     },
35466     
35467     getColumnIndexByDataIndex : function(dataIndex){
35468         if(!this.indexMap){
35469             this.indexMap = this.buildIndexMap();
35470         }
35471         return this.indexMap.dataToCol[dataIndex];
35472     },
35473     
35474     /**
35475      * Set a css style for a column dynamically. 
35476      * @param {Number} colIndex The index of the column
35477      * @param {String} name The css property name
35478      * @param {String} value The css value
35479      */
35480     setCSSStyle : function(colIndex, name, value){
35481         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
35482         Roo.util.CSS.updateRule(selector, name, value);
35483     },
35484     
35485     generateRules : function(cm){
35486         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
35487         Roo.util.CSS.removeStyleSheet(rulesId);
35488         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
35489             var cid = cm.getColumnId(i);
35490             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
35491                          this.tdSelector, cid, " {\n}\n",
35492                          this.hdSelector, cid, " {\n}\n",
35493                          this.splitSelector, cid, " {\n}\n");
35494         }
35495         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
35496     }
35497 });/*
35498  * Based on:
35499  * Ext JS Library 1.1.1
35500  * Copyright(c) 2006-2007, Ext JS, LLC.
35501  *
35502  * Originally Released Under LGPL - original licence link has changed is not relivant.
35503  *
35504  * Fork - LGPL
35505  * <script type="text/javascript">
35506  */
35507
35508 // private
35509 // This is a support class used internally by the Grid components
35510 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
35511     this.grid = grid;
35512     this.view = grid.getView();
35513     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
35514     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
35515     if(hd2){
35516         this.setHandleElId(Roo.id(hd));
35517         this.setOuterHandleElId(Roo.id(hd2));
35518     }
35519     this.scroll = false;
35520 };
35521 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
35522     maxDragWidth: 120,
35523     getDragData : function(e){
35524         var t = Roo.lib.Event.getTarget(e);
35525         var h = this.view.findHeaderCell(t);
35526         if(h){
35527             return {ddel: h.firstChild, header:h};
35528         }
35529         return false;
35530     },
35531
35532     onInitDrag : function(e){
35533         this.view.headersDisabled = true;
35534         var clone = this.dragData.ddel.cloneNode(true);
35535         clone.id = Roo.id();
35536         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
35537         this.proxy.update(clone);
35538         return true;
35539     },
35540
35541     afterValidDrop : function(){
35542         var v = this.view;
35543         setTimeout(function(){
35544             v.headersDisabled = false;
35545         }, 50);
35546     },
35547
35548     afterInvalidDrop : function(){
35549         var v = this.view;
35550         setTimeout(function(){
35551             v.headersDisabled = false;
35552         }, 50);
35553     }
35554 });
35555 /*
35556  * Based on:
35557  * Ext JS Library 1.1.1
35558  * Copyright(c) 2006-2007, Ext JS, LLC.
35559  *
35560  * Originally Released Under LGPL - original licence link has changed is not relivant.
35561  *
35562  * Fork - LGPL
35563  * <script type="text/javascript">
35564  */
35565 // private
35566 // This is a support class used internally by the Grid components
35567 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
35568     this.grid = grid;
35569     this.view = grid.getView();
35570     // split the proxies so they don't interfere with mouse events
35571     this.proxyTop = Roo.DomHelper.append(document.body, {
35572         cls:"col-move-top", html:"&#160;"
35573     }, true);
35574     this.proxyBottom = Roo.DomHelper.append(document.body, {
35575         cls:"col-move-bottom", html:"&#160;"
35576     }, true);
35577     this.proxyTop.hide = this.proxyBottom.hide = function(){
35578         this.setLeftTop(-100,-100);
35579         this.setStyle("visibility", "hidden");
35580     };
35581     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
35582     // temporarily disabled
35583     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
35584     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
35585 };
35586 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
35587     proxyOffsets : [-4, -9],
35588     fly: Roo.Element.fly,
35589
35590     getTargetFromEvent : function(e){
35591         var t = Roo.lib.Event.getTarget(e);
35592         var cindex = this.view.findCellIndex(t);
35593         if(cindex !== false){
35594             return this.view.getHeaderCell(cindex);
35595         }
35596         return null;
35597     },
35598
35599     nextVisible : function(h){
35600         var v = this.view, cm = this.grid.colModel;
35601         h = h.nextSibling;
35602         while(h){
35603             if(!cm.isHidden(v.getCellIndex(h))){
35604                 return h;
35605             }
35606             h = h.nextSibling;
35607         }
35608         return null;
35609     },
35610
35611     prevVisible : function(h){
35612         var v = this.view, cm = this.grid.colModel;
35613         h = h.prevSibling;
35614         while(h){
35615             if(!cm.isHidden(v.getCellIndex(h))){
35616                 return h;
35617             }
35618             h = h.prevSibling;
35619         }
35620         return null;
35621     },
35622
35623     positionIndicator : function(h, n, e){
35624         var x = Roo.lib.Event.getPageX(e);
35625         var r = Roo.lib.Dom.getRegion(n.firstChild);
35626         var px, pt, py = r.top + this.proxyOffsets[1];
35627         if((r.right - x) <= (r.right-r.left)/2){
35628             px = r.right+this.view.borderWidth;
35629             pt = "after";
35630         }else{
35631             px = r.left;
35632             pt = "before";
35633         }
35634         var oldIndex = this.view.getCellIndex(h);
35635         var newIndex = this.view.getCellIndex(n);
35636
35637         if(this.grid.colModel.isFixed(newIndex)){
35638             return false;
35639         }
35640
35641         var locked = this.grid.colModel.isLocked(newIndex);
35642
35643         if(pt == "after"){
35644             newIndex++;
35645         }
35646         if(oldIndex < newIndex){
35647             newIndex--;
35648         }
35649         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
35650             return false;
35651         }
35652         px +=  this.proxyOffsets[0];
35653         this.proxyTop.setLeftTop(px, py);
35654         this.proxyTop.show();
35655         if(!this.bottomOffset){
35656             this.bottomOffset = this.view.mainHd.getHeight();
35657         }
35658         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
35659         this.proxyBottom.show();
35660         return pt;
35661     },
35662
35663     onNodeEnter : function(n, dd, e, data){
35664         if(data.header != n){
35665             this.positionIndicator(data.header, n, e);
35666         }
35667     },
35668
35669     onNodeOver : function(n, dd, e, data){
35670         var result = false;
35671         if(data.header != n){
35672             result = this.positionIndicator(data.header, n, e);
35673         }
35674         if(!result){
35675             this.proxyTop.hide();
35676             this.proxyBottom.hide();
35677         }
35678         return result ? this.dropAllowed : this.dropNotAllowed;
35679     },
35680
35681     onNodeOut : function(n, dd, e, data){
35682         this.proxyTop.hide();
35683         this.proxyBottom.hide();
35684     },
35685
35686     onNodeDrop : function(n, dd, e, data){
35687         var h = data.header;
35688         if(h != n){
35689             var cm = this.grid.colModel;
35690             var x = Roo.lib.Event.getPageX(e);
35691             var r = Roo.lib.Dom.getRegion(n.firstChild);
35692             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
35693             var oldIndex = this.view.getCellIndex(h);
35694             var newIndex = this.view.getCellIndex(n);
35695             var locked = cm.isLocked(newIndex);
35696             if(pt == "after"){
35697                 newIndex++;
35698             }
35699             if(oldIndex < newIndex){
35700                 newIndex--;
35701             }
35702             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
35703                 return false;
35704             }
35705             cm.setLocked(oldIndex, locked, true);
35706             cm.moveColumn(oldIndex, newIndex);
35707             this.grid.fireEvent("columnmove", oldIndex, newIndex);
35708             return true;
35709         }
35710         return false;
35711     }
35712 });
35713 /*
35714  * Based on:
35715  * Ext JS Library 1.1.1
35716  * Copyright(c) 2006-2007, Ext JS, LLC.
35717  *
35718  * Originally Released Under LGPL - original licence link has changed is not relivant.
35719  *
35720  * Fork - LGPL
35721  * <script type="text/javascript">
35722  */
35723   
35724 /**
35725  * @class Roo.grid.GridView
35726  * @extends Roo.util.Observable
35727  *
35728  * @constructor
35729  * @param {Object} config
35730  */
35731 Roo.grid.GridView = function(config){
35732     Roo.grid.GridView.superclass.constructor.call(this);
35733     this.el = null;
35734
35735     Roo.apply(this, config);
35736 };
35737
35738 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
35739
35740     unselectable :  'unselectable="on"',
35741     unselectableCls :  'x-unselectable',
35742     
35743     
35744     rowClass : "x-grid-row",
35745
35746     cellClass : "x-grid-col",
35747
35748     tdClass : "x-grid-td",
35749
35750     hdClass : "x-grid-hd",
35751
35752     splitClass : "x-grid-split",
35753
35754     sortClasses : ["sort-asc", "sort-desc"],
35755
35756     enableMoveAnim : false,
35757
35758     hlColor: "C3DAF9",
35759
35760     dh : Roo.DomHelper,
35761
35762     fly : Roo.Element.fly,
35763
35764     css : Roo.util.CSS,
35765
35766     borderWidth: 1,
35767
35768     splitOffset: 3,
35769
35770     scrollIncrement : 22,
35771
35772     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
35773
35774     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
35775
35776     bind : function(ds, cm){
35777         if(this.ds){
35778             this.ds.un("load", this.onLoad, this);
35779             this.ds.un("datachanged", this.onDataChange, this);
35780             this.ds.un("add", this.onAdd, this);
35781             this.ds.un("remove", this.onRemove, this);
35782             this.ds.un("update", this.onUpdate, this);
35783             this.ds.un("clear", this.onClear, this);
35784         }
35785         if(ds){
35786             ds.on("load", this.onLoad, this);
35787             ds.on("datachanged", this.onDataChange, this);
35788             ds.on("add", this.onAdd, this);
35789             ds.on("remove", this.onRemove, this);
35790             ds.on("update", this.onUpdate, this);
35791             ds.on("clear", this.onClear, this);
35792         }
35793         this.ds = ds;
35794
35795         if(this.cm){
35796             this.cm.un("widthchange", this.onColWidthChange, this);
35797             this.cm.un("headerchange", this.onHeaderChange, this);
35798             this.cm.un("hiddenchange", this.onHiddenChange, this);
35799             this.cm.un("columnmoved", this.onColumnMove, this);
35800             this.cm.un("columnlockchange", this.onColumnLock, this);
35801         }
35802         if(cm){
35803             this.generateRules(cm);
35804             cm.on("widthchange", this.onColWidthChange, this);
35805             cm.on("headerchange", this.onHeaderChange, this);
35806             cm.on("hiddenchange", this.onHiddenChange, this);
35807             cm.on("columnmoved", this.onColumnMove, this);
35808             cm.on("columnlockchange", this.onColumnLock, this);
35809         }
35810         this.cm = cm;
35811     },
35812
35813     init: function(grid){
35814         Roo.grid.GridView.superclass.init.call(this, grid);
35815
35816         this.bind(grid.dataSource, grid.colModel);
35817
35818         grid.on("headerclick", this.handleHeaderClick, this);
35819
35820         if(grid.trackMouseOver){
35821             grid.on("mouseover", this.onRowOver, this);
35822             grid.on("mouseout", this.onRowOut, this);
35823         }
35824         grid.cancelTextSelection = function(){};
35825         this.gridId = grid.id;
35826
35827         var tpls = this.templates || {};
35828
35829         if(!tpls.master){
35830             tpls.master = new Roo.Template(
35831                '<div class="x-grid" hidefocus="true">',
35832                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
35833                   '<div class="x-grid-topbar"></div>',
35834                   '<div class="x-grid-scroller"><div></div></div>',
35835                   '<div class="x-grid-locked">',
35836                       '<div class="x-grid-header">{lockedHeader}</div>',
35837                       '<div class="x-grid-body">{lockedBody}</div>',
35838                   "</div>",
35839                   '<div class="x-grid-viewport">',
35840                       '<div class="x-grid-header">{header}</div>',
35841                       '<div class="x-grid-body">{body}</div>',
35842                   "</div>",
35843                   '<div class="x-grid-bottombar"></div>',
35844                  
35845                   '<div class="x-grid-resize-proxy">&#160;</div>',
35846                "</div>"
35847             );
35848             tpls.master.disableformats = true;
35849         }
35850
35851         if(!tpls.header){
35852             tpls.header = new Roo.Template(
35853                '<table border="0" cellspacing="0" cellpadding="0">',
35854                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
35855                "</table>{splits}"
35856             );
35857             tpls.header.disableformats = true;
35858         }
35859         tpls.header.compile();
35860
35861         if(!tpls.hcell){
35862             tpls.hcell = new Roo.Template(
35863                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
35864                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
35865                 "</div></td>"
35866              );
35867              tpls.hcell.disableFormats = true;
35868         }
35869         tpls.hcell.compile();
35870
35871         if(!tpls.hsplit){
35872             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
35873                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
35874             tpls.hsplit.disableFormats = true;
35875         }
35876         tpls.hsplit.compile();
35877
35878         if(!tpls.body){
35879             tpls.body = new Roo.Template(
35880                '<table border="0" cellspacing="0" cellpadding="0">',
35881                "<tbody>{rows}</tbody>",
35882                "</table>"
35883             );
35884             tpls.body.disableFormats = true;
35885         }
35886         tpls.body.compile();
35887
35888         if(!tpls.row){
35889             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
35890             tpls.row.disableFormats = true;
35891         }
35892         tpls.row.compile();
35893
35894         if(!tpls.cell){
35895             tpls.cell = new Roo.Template(
35896                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
35897                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
35898                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
35899                 "</td>"
35900             );
35901             tpls.cell.disableFormats = true;
35902         }
35903         tpls.cell.compile();
35904
35905         this.templates = tpls;
35906     },
35907
35908     // remap these for backwards compat
35909     onColWidthChange : function(){
35910         this.updateColumns.apply(this, arguments);
35911     },
35912     onHeaderChange : function(){
35913         this.updateHeaders.apply(this, arguments);
35914     }, 
35915     onHiddenChange : function(){
35916         this.handleHiddenChange.apply(this, arguments);
35917     },
35918     onColumnMove : function(){
35919         this.handleColumnMove.apply(this, arguments);
35920     },
35921     onColumnLock : function(){
35922         this.handleLockChange.apply(this, arguments);
35923     },
35924
35925     onDataChange : function(){
35926         this.refresh();
35927         this.updateHeaderSortState();
35928     },
35929
35930     onClear : function(){
35931         this.refresh();
35932     },
35933
35934     onUpdate : function(ds, record){
35935         this.refreshRow(record);
35936     },
35937
35938     refreshRow : function(record){
35939         var ds = this.ds, index;
35940         if(typeof record == 'number'){
35941             index = record;
35942             record = ds.getAt(index);
35943         }else{
35944             index = ds.indexOf(record);
35945         }
35946         this.insertRows(ds, index, index, true);
35947         this.onRemove(ds, record, index+1, true);
35948         this.syncRowHeights(index, index);
35949         this.layout();
35950         this.fireEvent("rowupdated", this, index, record);
35951     },
35952
35953     onAdd : function(ds, records, index){
35954         this.insertRows(ds, index, index + (records.length-1));
35955     },
35956
35957     onRemove : function(ds, record, index, isUpdate){
35958         if(isUpdate !== true){
35959             this.fireEvent("beforerowremoved", this, index, record);
35960         }
35961         var bt = this.getBodyTable(), lt = this.getLockedTable();
35962         if(bt.rows[index]){
35963             bt.firstChild.removeChild(bt.rows[index]);
35964         }
35965         if(lt.rows[index]){
35966             lt.firstChild.removeChild(lt.rows[index]);
35967         }
35968         if(isUpdate !== true){
35969             this.stripeRows(index);
35970             this.syncRowHeights(index, index);
35971             this.layout();
35972             this.fireEvent("rowremoved", this, index, record);
35973         }
35974     },
35975
35976     onLoad : function(){
35977         this.scrollToTop();
35978     },
35979
35980     /**
35981      * Scrolls the grid to the top
35982      */
35983     scrollToTop : function(){
35984         if(this.scroller){
35985             this.scroller.dom.scrollTop = 0;
35986             this.syncScroll();
35987         }
35988     },
35989
35990     /**
35991      * Gets a panel in the header of the grid that can be used for toolbars etc.
35992      * After modifying the contents of this panel a call to grid.autoSize() may be
35993      * required to register any changes in size.
35994      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
35995      * @return Roo.Element
35996      */
35997     getHeaderPanel : function(doShow){
35998         if(doShow){
35999             this.headerPanel.show();
36000         }
36001         return this.headerPanel;
36002     },
36003
36004     /**
36005      * Gets a panel in the footer of the grid that can be used for toolbars etc.
36006      * After modifying the contents of this panel a call to grid.autoSize() may be
36007      * required to register any changes in size.
36008      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
36009      * @return Roo.Element
36010      */
36011     getFooterPanel : function(doShow){
36012         if(doShow){
36013             this.footerPanel.show();
36014         }
36015         return this.footerPanel;
36016     },
36017
36018     initElements : function(){
36019         var E = Roo.Element;
36020         var el = this.grid.getGridEl().dom.firstChild;
36021         var cs = el.childNodes;
36022
36023         this.el = new E(el);
36024         
36025          this.focusEl = new E(el.firstChild);
36026         this.focusEl.swallowEvent("click", true);
36027         
36028         this.headerPanel = new E(cs[1]);
36029         this.headerPanel.enableDisplayMode("block");
36030
36031         this.scroller = new E(cs[2]);
36032         this.scrollSizer = new E(this.scroller.dom.firstChild);
36033
36034         this.lockedWrap = new E(cs[3]);
36035         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
36036         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
36037
36038         this.mainWrap = new E(cs[4]);
36039         this.mainHd = new E(this.mainWrap.dom.firstChild);
36040         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
36041
36042         this.footerPanel = new E(cs[5]);
36043         this.footerPanel.enableDisplayMode("block");
36044
36045         this.resizeProxy = new E(cs[6]);
36046
36047         this.headerSelector = String.format(
36048            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
36049            this.lockedHd.id, this.mainHd.id
36050         );
36051
36052         this.splitterSelector = String.format(
36053            '#{0} div.x-grid-split, #{1} div.x-grid-split',
36054            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
36055         );
36056     },
36057     idToCssName : function(s)
36058     {
36059         return s.replace(/[^a-z0-9]+/ig, '-');
36060     },
36061
36062     getHeaderCell : function(index){
36063         return Roo.DomQuery.select(this.headerSelector)[index];
36064     },
36065
36066     getHeaderCellMeasure : function(index){
36067         return this.getHeaderCell(index).firstChild;
36068     },
36069
36070     getHeaderCellText : function(index){
36071         return this.getHeaderCell(index).firstChild.firstChild;
36072     },
36073
36074     getLockedTable : function(){
36075         return this.lockedBody.dom.firstChild;
36076     },
36077
36078     getBodyTable : function(){
36079         return this.mainBody.dom.firstChild;
36080     },
36081
36082     getLockedRow : function(index){
36083         return this.getLockedTable().rows[index];
36084     },
36085
36086     getRow : function(index){
36087         return this.getBodyTable().rows[index];
36088     },
36089
36090     getRowComposite : function(index){
36091         if(!this.rowEl){
36092             this.rowEl = new Roo.CompositeElementLite();
36093         }
36094         var els = [], lrow, mrow;
36095         if(lrow = this.getLockedRow(index)){
36096             els.push(lrow);
36097         }
36098         if(mrow = this.getRow(index)){
36099             els.push(mrow);
36100         }
36101         this.rowEl.elements = els;
36102         return this.rowEl;
36103     },
36104     /**
36105      * Gets the 'td' of the cell
36106      * 
36107      * @param {Integer} rowIndex row to select
36108      * @param {Integer} colIndex column to select
36109      * 
36110      * @return {Object} 
36111      */
36112     getCell : function(rowIndex, colIndex){
36113         var locked = this.cm.getLockedCount();
36114         var source;
36115         if(colIndex < locked){
36116             source = this.lockedBody.dom.firstChild;
36117         }else{
36118             source = this.mainBody.dom.firstChild;
36119             colIndex -= locked;
36120         }
36121         return source.rows[rowIndex].childNodes[colIndex];
36122     },
36123
36124     getCellText : function(rowIndex, colIndex){
36125         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
36126     },
36127
36128     getCellBox : function(cell){
36129         var b = this.fly(cell).getBox();
36130         if(Roo.isOpera){ // opera fails to report the Y
36131             b.y = cell.offsetTop + this.mainBody.getY();
36132         }
36133         return b;
36134     },
36135
36136     getCellIndex : function(cell){
36137         var id = String(cell.className).match(this.cellRE);
36138         if(id){
36139             return parseInt(id[1], 10);
36140         }
36141         return 0;
36142     },
36143
36144     findHeaderIndex : function(n){
36145         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
36146         return r ? this.getCellIndex(r) : false;
36147     },
36148
36149     findHeaderCell : function(n){
36150         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
36151         return r ? r : false;
36152     },
36153
36154     findRowIndex : function(n){
36155         if(!n){
36156             return false;
36157         }
36158         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
36159         return r ? r.rowIndex : false;
36160     },
36161
36162     findCellIndex : function(node){
36163         var stop = this.el.dom;
36164         while(node && node != stop){
36165             if(this.findRE.test(node.className)){
36166                 return this.getCellIndex(node);
36167             }
36168             node = node.parentNode;
36169         }
36170         return false;
36171     },
36172
36173     getColumnId : function(index){
36174         return this.cm.getColumnId(index);
36175     },
36176
36177     getSplitters : function()
36178     {
36179         if(this.splitterSelector){
36180            return Roo.DomQuery.select(this.splitterSelector);
36181         }else{
36182             return null;
36183       }
36184     },
36185
36186     getSplitter : function(index){
36187         return this.getSplitters()[index];
36188     },
36189
36190     onRowOver : function(e, t){
36191         var row;
36192         if((row = this.findRowIndex(t)) !== false){
36193             this.getRowComposite(row).addClass("x-grid-row-over");
36194         }
36195     },
36196
36197     onRowOut : function(e, t){
36198         var row;
36199         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
36200             this.getRowComposite(row).removeClass("x-grid-row-over");
36201         }
36202     },
36203
36204     renderHeaders : function(){
36205         var cm = this.cm;
36206         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
36207         var cb = [], lb = [], sb = [], lsb = [], p = {};
36208         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
36209             p.cellId = "x-grid-hd-0-" + i;
36210             p.splitId = "x-grid-csplit-0-" + i;
36211             p.id = cm.getColumnId(i);
36212             p.title = cm.getColumnTooltip(i) || "";
36213             p.value = cm.getColumnHeader(i) || "";
36214             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
36215             if(!cm.isLocked(i)){
36216                 cb[cb.length] = ct.apply(p);
36217                 sb[sb.length] = st.apply(p);
36218             }else{
36219                 lb[lb.length] = ct.apply(p);
36220                 lsb[lsb.length] = st.apply(p);
36221             }
36222         }
36223         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
36224                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
36225     },
36226
36227     updateHeaders : function(){
36228         var html = this.renderHeaders();
36229         this.lockedHd.update(html[0]);
36230         this.mainHd.update(html[1]);
36231     },
36232
36233     /**
36234      * Focuses the specified row.
36235      * @param {Number} row The row index
36236      */
36237     focusRow : function(row)
36238     {
36239         //Roo.log('GridView.focusRow');
36240         var x = this.scroller.dom.scrollLeft;
36241         this.focusCell(row, 0, false);
36242         this.scroller.dom.scrollLeft = x;
36243     },
36244
36245     /**
36246      * Focuses the specified cell.
36247      * @param {Number} row The row index
36248      * @param {Number} col The column index
36249      * @param {Boolean} hscroll false to disable horizontal scrolling
36250      */
36251     focusCell : function(row, col, hscroll)
36252     {
36253         //Roo.log('GridView.focusCell');
36254         var el = this.ensureVisible(row, col, hscroll);
36255         this.focusEl.alignTo(el, "tl-tl");
36256         if(Roo.isGecko){
36257             this.focusEl.focus();
36258         }else{
36259             this.focusEl.focus.defer(1, this.focusEl);
36260         }
36261     },
36262
36263     /**
36264      * Scrolls the specified cell into view
36265      * @param {Number} row The row index
36266      * @param {Number} col The column index
36267      * @param {Boolean} hscroll false to disable horizontal scrolling
36268      */
36269     ensureVisible : function(row, col, hscroll)
36270     {
36271         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
36272         //return null; //disable for testing.
36273         if(typeof row != "number"){
36274             row = row.rowIndex;
36275         }
36276         if(row < 0 && row >= this.ds.getCount()){
36277             return  null;
36278         }
36279         col = (col !== undefined ? col : 0);
36280         var cm = this.grid.colModel;
36281         while(cm.isHidden(col)){
36282             col++;
36283         }
36284
36285         var el = this.getCell(row, col);
36286         if(!el){
36287             return null;
36288         }
36289         var c = this.scroller.dom;
36290
36291         var ctop = parseInt(el.offsetTop, 10);
36292         var cleft = parseInt(el.offsetLeft, 10);
36293         var cbot = ctop + el.offsetHeight;
36294         var cright = cleft + el.offsetWidth;
36295         
36296         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
36297         var stop = parseInt(c.scrollTop, 10);
36298         var sleft = parseInt(c.scrollLeft, 10);
36299         var sbot = stop + ch;
36300         var sright = sleft + c.clientWidth;
36301         /*
36302         Roo.log('GridView.ensureVisible:' +
36303                 ' ctop:' + ctop +
36304                 ' c.clientHeight:' + c.clientHeight +
36305                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
36306                 ' stop:' + stop +
36307                 ' cbot:' + cbot +
36308                 ' sbot:' + sbot +
36309                 ' ch:' + ch  
36310                 );
36311         */
36312         if(ctop < stop){
36313              c.scrollTop = ctop;
36314             //Roo.log("set scrolltop to ctop DISABLE?");
36315         }else if(cbot > sbot){
36316             //Roo.log("set scrolltop to cbot-ch");
36317             c.scrollTop = cbot-ch;
36318         }
36319         
36320         if(hscroll !== false){
36321             if(cleft < sleft){
36322                 c.scrollLeft = cleft;
36323             }else if(cright > sright){
36324                 c.scrollLeft = cright-c.clientWidth;
36325             }
36326         }
36327          
36328         return el;
36329     },
36330
36331     updateColumns : function(){
36332         this.grid.stopEditing();
36333         var cm = this.grid.colModel, colIds = this.getColumnIds();
36334         //var totalWidth = cm.getTotalWidth();
36335         var pos = 0;
36336         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
36337             //if(cm.isHidden(i)) continue;
36338             var w = cm.getColumnWidth(i);
36339             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
36340             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
36341         }
36342         this.updateSplitters();
36343     },
36344
36345     generateRules : function(cm){
36346         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
36347         Roo.util.CSS.removeStyleSheet(rulesId);
36348         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
36349             var cid = cm.getColumnId(i);
36350             var align = '';
36351             if(cm.config[i].align){
36352                 align = 'text-align:'+cm.config[i].align+';';
36353             }
36354             var hidden = '';
36355             if(cm.isHidden(i)){
36356                 hidden = 'display:none;';
36357             }
36358             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
36359             ruleBuf.push(
36360                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
36361                     this.hdSelector, cid, " {\n", align, width, "}\n",
36362                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
36363                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
36364         }
36365         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
36366     },
36367
36368     updateSplitters : function(){
36369         var cm = this.cm, s = this.getSplitters();
36370         if(s){ // splitters not created yet
36371             var pos = 0, locked = true;
36372             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
36373                 if(cm.isHidden(i)) continue;
36374                 var w = cm.getColumnWidth(i); // make sure it's a number
36375                 if(!cm.isLocked(i) && locked){
36376                     pos = 0;
36377                     locked = false;
36378                 }
36379                 pos += w;
36380                 s[i].style.left = (pos-this.splitOffset) + "px";
36381             }
36382         }
36383     },
36384
36385     handleHiddenChange : function(colModel, colIndex, hidden){
36386         if(hidden){
36387             this.hideColumn(colIndex);
36388         }else{
36389             this.unhideColumn(colIndex);
36390         }
36391     },
36392
36393     hideColumn : function(colIndex){
36394         var cid = this.getColumnId(colIndex);
36395         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
36396         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
36397         if(Roo.isSafari){
36398             this.updateHeaders();
36399         }
36400         this.updateSplitters();
36401         this.layout();
36402     },
36403
36404     unhideColumn : function(colIndex){
36405         var cid = this.getColumnId(colIndex);
36406         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
36407         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
36408
36409         if(Roo.isSafari){
36410             this.updateHeaders();
36411         }
36412         this.updateSplitters();
36413         this.layout();
36414     },
36415
36416     insertRows : function(dm, firstRow, lastRow, isUpdate){
36417         if(firstRow == 0 && lastRow == dm.getCount()-1){
36418             this.refresh();
36419         }else{
36420             if(!isUpdate){
36421                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
36422             }
36423             var s = this.getScrollState();
36424             var markup = this.renderRows(firstRow, lastRow);
36425             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
36426             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
36427             this.restoreScroll(s);
36428             if(!isUpdate){
36429                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
36430                 this.syncRowHeights(firstRow, lastRow);
36431                 this.stripeRows(firstRow);
36432                 this.layout();
36433             }
36434         }
36435     },
36436
36437     bufferRows : function(markup, target, index){
36438         var before = null, trows = target.rows, tbody = target.tBodies[0];
36439         if(index < trows.length){
36440             before = trows[index];
36441         }
36442         var b = document.createElement("div");
36443         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
36444         var rows = b.firstChild.rows;
36445         for(var i = 0, len = rows.length; i < len; i++){
36446             if(before){
36447                 tbody.insertBefore(rows[0], before);
36448             }else{
36449                 tbody.appendChild(rows[0]);
36450             }
36451         }
36452         b.innerHTML = "";
36453         b = null;
36454     },
36455
36456     deleteRows : function(dm, firstRow, lastRow){
36457         if(dm.getRowCount()<1){
36458             this.fireEvent("beforerefresh", this);
36459             this.mainBody.update("");
36460             this.lockedBody.update("");
36461             this.fireEvent("refresh", this);
36462         }else{
36463             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
36464             var bt = this.getBodyTable();
36465             var tbody = bt.firstChild;
36466             var rows = bt.rows;
36467             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
36468                 tbody.removeChild(rows[firstRow]);
36469             }
36470             this.stripeRows(firstRow);
36471             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
36472         }
36473     },
36474
36475     updateRows : function(dataSource, firstRow, lastRow){
36476         var s = this.getScrollState();
36477         this.refresh();
36478         this.restoreScroll(s);
36479     },
36480
36481     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
36482         if(!noRefresh){
36483            this.refresh();
36484         }
36485         this.updateHeaderSortState();
36486     },
36487
36488     getScrollState : function(){
36489         
36490         var sb = this.scroller.dom;
36491         return {left: sb.scrollLeft, top: sb.scrollTop};
36492     },
36493
36494     stripeRows : function(startRow){
36495         if(!this.grid.stripeRows || this.ds.getCount() < 1){
36496             return;
36497         }
36498         startRow = startRow || 0;
36499         var rows = this.getBodyTable().rows;
36500         var lrows = this.getLockedTable().rows;
36501         var cls = ' x-grid-row-alt ';
36502         for(var i = startRow, len = rows.length; i < len; i++){
36503             var row = rows[i], lrow = lrows[i];
36504             var isAlt = ((i+1) % 2 == 0);
36505             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
36506             if(isAlt == hasAlt){
36507                 continue;
36508             }
36509             if(isAlt){
36510                 row.className += " x-grid-row-alt";
36511             }else{
36512                 row.className = row.className.replace("x-grid-row-alt", "");
36513             }
36514             if(lrow){
36515                 lrow.className = row.className;
36516             }
36517         }
36518     },
36519
36520     restoreScroll : function(state){
36521         //Roo.log('GridView.restoreScroll');
36522         var sb = this.scroller.dom;
36523         sb.scrollLeft = state.left;
36524         sb.scrollTop = state.top;
36525         this.syncScroll();
36526     },
36527
36528     syncScroll : function(){
36529         //Roo.log('GridView.syncScroll');
36530         var sb = this.scroller.dom;
36531         var sh = this.mainHd.dom;
36532         var bs = this.mainBody.dom;
36533         var lv = this.lockedBody.dom;
36534         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
36535         lv.scrollTop = bs.scrollTop = sb.scrollTop;
36536     },
36537
36538     handleScroll : function(e){
36539         this.syncScroll();
36540         var sb = this.scroller.dom;
36541         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
36542         e.stopEvent();
36543     },
36544
36545     handleWheel : function(e){
36546         var d = e.getWheelDelta();
36547         this.scroller.dom.scrollTop -= d*22;
36548         // set this here to prevent jumpy scrolling on large tables
36549         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
36550         e.stopEvent();
36551     },
36552
36553     renderRows : function(startRow, endRow){
36554         // pull in all the crap needed to render rows
36555         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
36556         var colCount = cm.getColumnCount();
36557
36558         if(ds.getCount() < 1){
36559             return ["", ""];
36560         }
36561
36562         // build a map for all the columns
36563         var cs = [];
36564         for(var i = 0; i < colCount; i++){
36565             var name = cm.getDataIndex(i);
36566             cs[i] = {
36567                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
36568                 renderer : cm.getRenderer(i),
36569                 id : cm.getColumnId(i),
36570                 locked : cm.isLocked(i)
36571             };
36572         }
36573
36574         startRow = startRow || 0;
36575         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
36576
36577         // records to render
36578         var rs = ds.getRange(startRow, endRow);
36579
36580         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
36581     },
36582
36583     // As much as I hate to duplicate code, this was branched because FireFox really hates
36584     // [].join("") on strings. The performance difference was substantial enough to
36585     // branch this function
36586     doRender : Roo.isGecko ?
36587             function(cs, rs, ds, startRow, colCount, stripe){
36588                 var ts = this.templates, ct = ts.cell, rt = ts.row;
36589                 // buffers
36590                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
36591                 
36592                 var hasListener = this.grid.hasListener('rowclass');
36593                 var rowcfg = {};
36594                 for(var j = 0, len = rs.length; j < len; j++){
36595                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
36596                     for(var i = 0; i < colCount; i++){
36597                         c = cs[i];
36598                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
36599                         p.id = c.id;
36600                         p.css = p.attr = "";
36601                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
36602                         if(p.value == undefined || p.value === "") p.value = "&#160;";
36603                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
36604                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
36605                         }
36606                         var markup = ct.apply(p);
36607                         if(!c.locked){
36608                             cb+= markup;
36609                         }else{
36610                             lcb+= markup;
36611                         }
36612                     }
36613                     var alt = [];
36614                     if(stripe && ((rowIndex+1) % 2 == 0)){
36615                         alt.push("x-grid-row-alt")
36616                     }
36617                     if(r.dirty){
36618                         alt.push(  " x-grid-dirty-row");
36619                     }
36620                     rp.cells = lcb;
36621                     if(this.getRowClass){
36622                         alt.push(this.getRowClass(r, rowIndex));
36623                     }
36624                     if (hasListener) {
36625                         rowcfg = {
36626                              
36627                             record: r,
36628                             rowIndex : rowIndex,
36629                             rowClass : ''
36630                         }
36631                         this.grid.fireEvent('rowclass', this, rowcfg);
36632                         alt.push(rowcfg.rowClass);
36633                     }
36634                     rp.alt = alt.join(" ");
36635                     lbuf+= rt.apply(rp);
36636                     rp.cells = cb;
36637                     buf+=  rt.apply(rp);
36638                 }
36639                 return [lbuf, buf];
36640             } :
36641             function(cs, rs, ds, startRow, colCount, stripe){
36642                 var ts = this.templates, ct = ts.cell, rt = ts.row;
36643                 // buffers
36644                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
36645                 var hasListener = this.grid.hasListener('rowclass');
36646  
36647                 var rowcfg = {};
36648                 for(var j = 0, len = rs.length; j < len; j++){
36649                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
36650                     for(var i = 0; i < colCount; i++){
36651                         c = cs[i];
36652                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
36653                         p.id = c.id;
36654                         p.css = p.attr = "";
36655                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
36656                         if(p.value == undefined || p.value === "") p.value = "&#160;";
36657                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
36658                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
36659                         }
36660                         
36661                         var markup = ct.apply(p);
36662                         if(!c.locked){
36663                             cb[cb.length] = markup;
36664                         }else{
36665                             lcb[lcb.length] = markup;
36666                         }
36667                     }
36668                     var alt = [];
36669                     if(stripe && ((rowIndex+1) % 2 == 0)){
36670                         alt.push( "x-grid-row-alt");
36671                     }
36672                     if(r.dirty){
36673                         alt.push(" x-grid-dirty-row");
36674                     }
36675                     rp.cells = lcb;
36676                     if(this.getRowClass){
36677                         alt.push( this.getRowClass(r, rowIndex));
36678                     }
36679                     if (hasListener) {
36680                         rowcfg = {
36681                              
36682                             record: r,
36683                             rowIndex : rowIndex,
36684                             rowClass : ''
36685                         }
36686                         this.grid.fireEvent('rowclass', this, rowcfg);
36687                         alt.push(rowcfg.rowClass);
36688                     }
36689                     rp.alt = alt.join(" ");
36690                     rp.cells = lcb.join("");
36691                     lbuf[lbuf.length] = rt.apply(rp);
36692                     rp.cells = cb.join("");
36693                     buf[buf.length] =  rt.apply(rp);
36694                 }
36695                 return [lbuf.join(""), buf.join("")];
36696             },
36697
36698     renderBody : function(){
36699         var markup = this.renderRows();
36700         var bt = this.templates.body;
36701         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
36702     },
36703
36704     /**
36705      * Refreshes the grid
36706      * @param {Boolean} headersToo
36707      */
36708     refresh : function(headersToo){
36709         this.fireEvent("beforerefresh", this);
36710         this.grid.stopEditing();
36711         var result = this.renderBody();
36712         this.lockedBody.update(result[0]);
36713         this.mainBody.update(result[1]);
36714         if(headersToo === true){
36715             this.updateHeaders();
36716             this.updateColumns();
36717             this.updateSplitters();
36718             this.updateHeaderSortState();
36719         }
36720         this.syncRowHeights();
36721         this.layout();
36722         this.fireEvent("refresh", this);
36723     },
36724
36725     handleColumnMove : function(cm, oldIndex, newIndex){
36726         this.indexMap = null;
36727         var s = this.getScrollState();
36728         this.refresh(true);
36729         this.restoreScroll(s);
36730         this.afterMove(newIndex);
36731     },
36732
36733     afterMove : function(colIndex){
36734         if(this.enableMoveAnim && Roo.enableFx){
36735             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
36736         }
36737         // if multisort - fix sortOrder, and reload..
36738         if (this.grid.dataSource.multiSort) {
36739             // the we can call sort again..
36740             var dm = this.grid.dataSource;
36741             var cm = this.grid.colModel;
36742             var so = [];
36743             for(var i = 0; i < cm.config.length; i++ ) {
36744                 
36745                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
36746                     continue; // dont' bother, it's not in sort list or being set.
36747                 }
36748                 
36749                 so.push(cm.config[i].dataIndex);
36750             };
36751             dm.sortOrder = so;
36752             dm.load(dm.lastOptions);
36753             
36754             
36755         }
36756         
36757     },
36758
36759     updateCell : function(dm, rowIndex, dataIndex){
36760         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
36761         if(typeof colIndex == "undefined"){ // not present in grid
36762             return;
36763         }
36764         var cm = this.grid.colModel;
36765         var cell = this.getCell(rowIndex, colIndex);
36766         var cellText = this.getCellText(rowIndex, colIndex);
36767
36768         var p = {
36769             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
36770             id : cm.getColumnId(colIndex),
36771             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
36772         };
36773         var renderer = cm.getRenderer(colIndex);
36774         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
36775         if(typeof val == "undefined" || val === "") val = "&#160;";
36776         cellText.innerHTML = val;
36777         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
36778         this.syncRowHeights(rowIndex, rowIndex);
36779     },
36780
36781     calcColumnWidth : function(colIndex, maxRowsToMeasure){
36782         var maxWidth = 0;
36783         if(this.grid.autoSizeHeaders){
36784             var h = this.getHeaderCellMeasure(colIndex);
36785             maxWidth = Math.max(maxWidth, h.scrollWidth);
36786         }
36787         var tb, index;
36788         if(this.cm.isLocked(colIndex)){
36789             tb = this.getLockedTable();
36790             index = colIndex;
36791         }else{
36792             tb = this.getBodyTable();
36793             index = colIndex - this.cm.getLockedCount();
36794         }
36795         if(tb && tb.rows){
36796             var rows = tb.rows;
36797             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
36798             for(var i = 0; i < stopIndex; i++){
36799                 var cell = rows[i].childNodes[index].firstChild;
36800                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
36801             }
36802         }
36803         return maxWidth + /*margin for error in IE*/ 5;
36804     },
36805     /**
36806      * Autofit a column to its content.
36807      * @param {Number} colIndex
36808      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
36809      */
36810      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
36811          if(this.cm.isHidden(colIndex)){
36812              return; // can't calc a hidden column
36813          }
36814         if(forceMinSize){
36815             var cid = this.cm.getColumnId(colIndex);
36816             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
36817            if(this.grid.autoSizeHeaders){
36818                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
36819            }
36820         }
36821         var newWidth = this.calcColumnWidth(colIndex);
36822         this.cm.setColumnWidth(colIndex,
36823             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
36824         if(!suppressEvent){
36825             this.grid.fireEvent("columnresize", colIndex, newWidth);
36826         }
36827     },
36828
36829     /**
36830      * Autofits all columns to their content and then expands to fit any extra space in the grid
36831      */
36832      autoSizeColumns : function(){
36833         var cm = this.grid.colModel;
36834         var colCount = cm.getColumnCount();
36835         for(var i = 0; i < colCount; i++){
36836             this.autoSizeColumn(i, true, true);
36837         }
36838         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
36839             this.fitColumns();
36840         }else{
36841             this.updateColumns();
36842             this.layout();
36843         }
36844     },
36845
36846     /**
36847      * Autofits all columns to the grid's width proportionate with their current size
36848      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
36849      */
36850     fitColumns : function(reserveScrollSpace){
36851         var cm = this.grid.colModel;
36852         var colCount = cm.getColumnCount();
36853         var cols = [];
36854         var width = 0;
36855         var i, w;
36856         for (i = 0; i < colCount; i++){
36857             if(!cm.isHidden(i) && !cm.isFixed(i)){
36858                 w = cm.getColumnWidth(i);
36859                 cols.push(i);
36860                 cols.push(w);
36861                 width += w;
36862             }
36863         }
36864         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
36865         if(reserveScrollSpace){
36866             avail -= 17;
36867         }
36868         var frac = (avail - cm.getTotalWidth())/width;
36869         while (cols.length){
36870             w = cols.pop();
36871             i = cols.pop();
36872             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
36873         }
36874         this.updateColumns();
36875         this.layout();
36876     },
36877
36878     onRowSelect : function(rowIndex){
36879         var row = this.getRowComposite(rowIndex);
36880         row.addClass("x-grid-row-selected");
36881     },
36882
36883     onRowDeselect : function(rowIndex){
36884         var row = this.getRowComposite(rowIndex);
36885         row.removeClass("x-grid-row-selected");
36886     },
36887
36888     onCellSelect : function(row, col){
36889         var cell = this.getCell(row, col);
36890         if(cell){
36891             Roo.fly(cell).addClass("x-grid-cell-selected");
36892         }
36893     },
36894
36895     onCellDeselect : function(row, col){
36896         var cell = this.getCell(row, col);
36897         if(cell){
36898             Roo.fly(cell).removeClass("x-grid-cell-selected");
36899         }
36900     },
36901
36902     updateHeaderSortState : function(){
36903         
36904         // sort state can be single { field: xxx, direction : yyy}
36905         // or   { xxx=>ASC , yyy : DESC ..... }
36906         
36907         var mstate = {};
36908         if (!this.ds.multiSort) { 
36909             var state = this.ds.getSortState();
36910             if(!state){
36911                 return;
36912             }
36913             mstate[state.field] = state.direction;
36914             // FIXME... - this is not used here.. but might be elsewhere..
36915             this.sortState = state;
36916             
36917         } else {
36918             mstate = this.ds.sortToggle;
36919         }
36920         //remove existing sort classes..
36921         
36922         var sc = this.sortClasses;
36923         var hds = this.el.select(this.headerSelector).removeClass(sc);
36924         
36925         for(var f in mstate) {
36926         
36927             var sortColumn = this.cm.findColumnIndex(f);
36928             
36929             if(sortColumn != -1){
36930                 var sortDir = mstate[f];        
36931                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
36932             }
36933         }
36934         
36935          
36936         
36937     },
36938
36939
36940     handleHeaderClick : function(g, index){
36941         if(this.headersDisabled){
36942             return;
36943         }
36944         var dm = g.dataSource, cm = g.colModel;
36945         if(!cm.isSortable(index)){
36946             return;
36947         }
36948         g.stopEditing();
36949         
36950         if (dm.multiSort) {
36951             // update the sortOrder
36952             var so = [];
36953             for(var i = 0; i < cm.config.length; i++ ) {
36954                 
36955                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
36956                     continue; // dont' bother, it's not in sort list or being set.
36957                 }
36958                 
36959                 so.push(cm.config[i].dataIndex);
36960             };
36961             dm.sortOrder = so;
36962         }
36963         
36964         
36965         dm.sort(cm.getDataIndex(index));
36966     },
36967
36968
36969     destroy : function(){
36970         if(this.colMenu){
36971             this.colMenu.removeAll();
36972             Roo.menu.MenuMgr.unregister(this.colMenu);
36973             this.colMenu.getEl().remove();
36974             delete this.colMenu;
36975         }
36976         if(this.hmenu){
36977             this.hmenu.removeAll();
36978             Roo.menu.MenuMgr.unregister(this.hmenu);
36979             this.hmenu.getEl().remove();
36980             delete this.hmenu;
36981         }
36982         if(this.grid.enableColumnMove){
36983             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
36984             if(dds){
36985                 for(var dd in dds){
36986                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
36987                         var elid = dds[dd].dragElId;
36988                         dds[dd].unreg();
36989                         Roo.get(elid).remove();
36990                     } else if(dds[dd].config.isTarget){
36991                         dds[dd].proxyTop.remove();
36992                         dds[dd].proxyBottom.remove();
36993                         dds[dd].unreg();
36994                     }
36995                     if(Roo.dd.DDM.locationCache[dd]){
36996                         delete Roo.dd.DDM.locationCache[dd];
36997                     }
36998                 }
36999                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
37000             }
37001         }
37002         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
37003         this.bind(null, null);
37004         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
37005     },
37006
37007     handleLockChange : function(){
37008         this.refresh(true);
37009     },
37010
37011     onDenyColumnLock : function(){
37012
37013     },
37014
37015     onDenyColumnHide : function(){
37016
37017     },
37018
37019     handleHdMenuClick : function(item){
37020         var index = this.hdCtxIndex;
37021         var cm = this.cm, ds = this.ds;
37022         switch(item.id){
37023             case "asc":
37024                 ds.sort(cm.getDataIndex(index), "ASC");
37025                 break;
37026             case "desc":
37027                 ds.sort(cm.getDataIndex(index), "DESC");
37028                 break;
37029             case "lock":
37030                 var lc = cm.getLockedCount();
37031                 if(cm.getColumnCount(true) <= lc+1){
37032                     this.onDenyColumnLock();
37033                     return;
37034                 }
37035                 if(lc != index){
37036                     cm.setLocked(index, true, true);
37037                     cm.moveColumn(index, lc);
37038                     this.grid.fireEvent("columnmove", index, lc);
37039                 }else{
37040                     cm.setLocked(index, true);
37041                 }
37042             break;
37043             case "unlock":
37044                 var lc = cm.getLockedCount();
37045                 if((lc-1) != index){
37046                     cm.setLocked(index, false, true);
37047                     cm.moveColumn(index, lc-1);
37048                     this.grid.fireEvent("columnmove", index, lc-1);
37049                 }else{
37050                     cm.setLocked(index, false);
37051                 }
37052             break;
37053             default:
37054                 index = cm.getIndexById(item.id.substr(4));
37055                 if(index != -1){
37056                     if(item.checked && cm.getColumnCount(true) <= 1){
37057                         this.onDenyColumnHide();
37058                         return false;
37059                     }
37060                     cm.setHidden(index, item.checked);
37061                 }
37062         }
37063         return true;
37064     },
37065
37066     beforeColMenuShow : function(){
37067         var cm = this.cm,  colCount = cm.getColumnCount();
37068         this.colMenu.removeAll();
37069         for(var i = 0; i < colCount; i++){
37070             this.colMenu.add(new Roo.menu.CheckItem({
37071                 id: "col-"+cm.getColumnId(i),
37072                 text: cm.getColumnHeader(i),
37073                 checked: !cm.isHidden(i),
37074                 hideOnClick:false
37075             }));
37076         }
37077     },
37078
37079     handleHdCtx : function(g, index, e){
37080         e.stopEvent();
37081         var hd = this.getHeaderCell(index);
37082         this.hdCtxIndex = index;
37083         var ms = this.hmenu.items, cm = this.cm;
37084         ms.get("asc").setDisabled(!cm.isSortable(index));
37085         ms.get("desc").setDisabled(!cm.isSortable(index));
37086         if(this.grid.enableColLock !== false){
37087             ms.get("lock").setDisabled(cm.isLocked(index));
37088             ms.get("unlock").setDisabled(!cm.isLocked(index));
37089         }
37090         this.hmenu.show(hd, "tl-bl");
37091     },
37092
37093     handleHdOver : function(e){
37094         var hd = this.findHeaderCell(e.getTarget());
37095         if(hd && !this.headersDisabled){
37096             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
37097                this.fly(hd).addClass("x-grid-hd-over");
37098             }
37099         }
37100     },
37101
37102     handleHdOut : function(e){
37103         var hd = this.findHeaderCell(e.getTarget());
37104         if(hd){
37105             this.fly(hd).removeClass("x-grid-hd-over");
37106         }
37107     },
37108
37109     handleSplitDblClick : function(e, t){
37110         var i = this.getCellIndex(t);
37111         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
37112             this.autoSizeColumn(i, true);
37113             this.layout();
37114         }
37115     },
37116
37117     render : function(){
37118
37119         var cm = this.cm;
37120         var colCount = cm.getColumnCount();
37121
37122         if(this.grid.monitorWindowResize === true){
37123             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37124         }
37125         var header = this.renderHeaders();
37126         var body = this.templates.body.apply({rows:""});
37127         var html = this.templates.master.apply({
37128             lockedBody: body,
37129             body: body,
37130             lockedHeader: header[0],
37131             header: header[1]
37132         });
37133
37134         //this.updateColumns();
37135
37136         this.grid.getGridEl().dom.innerHTML = html;
37137
37138         this.initElements();
37139         
37140         // a kludge to fix the random scolling effect in webkit
37141         this.el.on("scroll", function() {
37142             this.el.dom.scrollTop=0; // hopefully not recursive..
37143         },this);
37144
37145         this.scroller.on("scroll", this.handleScroll, this);
37146         this.lockedBody.on("mousewheel", this.handleWheel, this);
37147         this.mainBody.on("mousewheel", this.handleWheel, this);
37148
37149         this.mainHd.on("mouseover", this.handleHdOver, this);
37150         this.mainHd.on("mouseout", this.handleHdOut, this);
37151         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
37152                 {delegate: "."+this.splitClass});
37153
37154         this.lockedHd.on("mouseover", this.handleHdOver, this);
37155         this.lockedHd.on("mouseout", this.handleHdOut, this);
37156         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
37157                 {delegate: "."+this.splitClass});
37158
37159         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
37160             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
37161         }
37162
37163         this.updateSplitters();
37164
37165         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
37166             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
37167             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
37168         }
37169
37170         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
37171             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
37172             this.hmenu.add(
37173                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
37174                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
37175             );
37176             if(this.grid.enableColLock !== false){
37177                 this.hmenu.add('-',
37178                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
37179                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
37180                 );
37181             }
37182             if(this.grid.enableColumnHide !== false){
37183
37184                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
37185                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
37186                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
37187
37188                 this.hmenu.add('-',
37189                     {id:"columns", text: this.columnsText, menu: this.colMenu}
37190                 );
37191             }
37192             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
37193
37194             this.grid.on("headercontextmenu", this.handleHdCtx, this);
37195         }
37196
37197         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
37198             this.dd = new Roo.grid.GridDragZone(this.grid, {
37199                 ddGroup : this.grid.ddGroup || 'GridDD'
37200             });
37201             
37202         }
37203
37204         /*
37205         for(var i = 0; i < colCount; i++){
37206             if(cm.isHidden(i)){
37207                 this.hideColumn(i);
37208             }
37209             if(cm.config[i].align){
37210                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
37211                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
37212             }
37213         }*/
37214         
37215         this.updateHeaderSortState();
37216
37217         this.beforeInitialResize();
37218         this.layout(true);
37219
37220         // two part rendering gives faster view to the user
37221         this.renderPhase2.defer(1, this);
37222     },
37223
37224     renderPhase2 : function(){
37225         // render the rows now
37226         this.refresh();
37227         if(this.grid.autoSizeColumns){
37228             this.autoSizeColumns();
37229         }
37230     },
37231
37232     beforeInitialResize : function(){
37233
37234     },
37235
37236     onColumnSplitterMoved : function(i, w){
37237         this.userResized = true;
37238         var cm = this.grid.colModel;
37239         cm.setColumnWidth(i, w, true);
37240         var cid = cm.getColumnId(i);
37241         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
37242         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
37243         this.updateSplitters();
37244         this.layout();
37245         this.grid.fireEvent("columnresize", i, w);
37246     },
37247
37248     syncRowHeights : function(startIndex, endIndex){
37249         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
37250             startIndex = startIndex || 0;
37251             var mrows = this.getBodyTable().rows;
37252             var lrows = this.getLockedTable().rows;
37253             var len = mrows.length-1;
37254             endIndex = Math.min(endIndex || len, len);
37255             for(var i = startIndex; i <= endIndex; i++){
37256                 var m = mrows[i], l = lrows[i];
37257                 var h = Math.max(m.offsetHeight, l.offsetHeight);
37258                 m.style.height = l.style.height = h + "px";
37259             }
37260         }
37261     },
37262
37263     layout : function(initialRender, is2ndPass){
37264         var g = this.grid;
37265         var auto = g.autoHeight;
37266         var scrollOffset = 16;
37267         var c = g.getGridEl(), cm = this.cm,
37268                 expandCol = g.autoExpandColumn,
37269                 gv = this;
37270         //c.beginMeasure();
37271
37272         if(!c.dom.offsetWidth){ // display:none?
37273             if(initialRender){
37274                 this.lockedWrap.show();
37275                 this.mainWrap.show();
37276             }
37277             return;
37278         }
37279
37280         var hasLock = this.cm.isLocked(0);
37281
37282         var tbh = this.headerPanel.getHeight();
37283         var bbh = this.footerPanel.getHeight();
37284
37285         if(auto){
37286             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
37287             var newHeight = ch + c.getBorderWidth("tb");
37288             if(g.maxHeight){
37289                 newHeight = Math.min(g.maxHeight, newHeight);
37290             }
37291             c.setHeight(newHeight);
37292         }
37293
37294         if(g.autoWidth){
37295             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
37296         }
37297
37298         var s = this.scroller;
37299
37300         var csize = c.getSize(true);
37301
37302         this.el.setSize(csize.width, csize.height);
37303
37304         this.headerPanel.setWidth(csize.width);
37305         this.footerPanel.setWidth(csize.width);
37306
37307         var hdHeight = this.mainHd.getHeight();
37308         var vw = csize.width;
37309         var vh = csize.height - (tbh + bbh);
37310
37311         s.setSize(vw, vh);
37312
37313         var bt = this.getBodyTable();
37314         var ltWidth = hasLock ?
37315                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
37316
37317         var scrollHeight = bt.offsetHeight;
37318         var scrollWidth = ltWidth + bt.offsetWidth;
37319         var vscroll = false, hscroll = false;
37320
37321         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
37322
37323         var lw = this.lockedWrap, mw = this.mainWrap;
37324         var lb = this.lockedBody, mb = this.mainBody;
37325
37326         setTimeout(function(){
37327             var t = s.dom.offsetTop;
37328             var w = s.dom.clientWidth,
37329                 h = s.dom.clientHeight;
37330
37331             lw.setTop(t);
37332             lw.setSize(ltWidth, h);
37333
37334             mw.setLeftTop(ltWidth, t);
37335             mw.setSize(w-ltWidth, h);
37336
37337             lb.setHeight(h-hdHeight);
37338             mb.setHeight(h-hdHeight);
37339
37340             if(is2ndPass !== true && !gv.userResized && expandCol){
37341                 // high speed resize without full column calculation
37342                 
37343                 var ci = cm.getIndexById(expandCol);
37344                 if (ci < 0) {
37345                     ci = cm.findColumnIndex(expandCol);
37346                 }
37347                 ci = Math.max(0, ci); // make sure it's got at least the first col.
37348                 var expandId = cm.getColumnId(ci);
37349                 var  tw = cm.getTotalWidth(false);
37350                 var currentWidth = cm.getColumnWidth(ci);
37351                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
37352                 if(currentWidth != cw){
37353                     cm.setColumnWidth(ci, cw, true);
37354                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
37355                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
37356                     gv.updateSplitters();
37357                     gv.layout(false, true);
37358                 }
37359             }
37360
37361             if(initialRender){
37362                 lw.show();
37363                 mw.show();
37364             }
37365             //c.endMeasure();
37366         }, 10);
37367     },
37368
37369     onWindowResize : function(){
37370         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
37371             return;
37372         }
37373         this.layout();
37374     },
37375
37376     appendFooter : function(parentEl){
37377         return null;
37378     },
37379
37380     sortAscText : "Sort Ascending",
37381     sortDescText : "Sort Descending",
37382     lockText : "Lock Column",
37383     unlockText : "Unlock Column",
37384     columnsText : "Columns"
37385 });
37386
37387
37388 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
37389     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
37390     this.proxy.el.addClass('x-grid3-col-dd');
37391 };
37392
37393 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
37394     handleMouseDown : function(e){
37395
37396     },
37397
37398     callHandleMouseDown : function(e){
37399         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
37400     }
37401 });
37402 /*
37403  * Based on:
37404  * Ext JS Library 1.1.1
37405  * Copyright(c) 2006-2007, Ext JS, LLC.
37406  *
37407  * Originally Released Under LGPL - original licence link has changed is not relivant.
37408  *
37409  * Fork - LGPL
37410  * <script type="text/javascript">
37411  */
37412  
37413 // private
37414 // This is a support class used internally by the Grid components
37415 Roo.grid.SplitDragZone = function(grid, hd, hd2){
37416     this.grid = grid;
37417     this.view = grid.getView();
37418     this.proxy = this.view.resizeProxy;
37419     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
37420         "gridSplitters" + this.grid.getGridEl().id, {
37421         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
37422     });
37423     this.setHandleElId(Roo.id(hd));
37424     this.setOuterHandleElId(Roo.id(hd2));
37425     this.scroll = false;
37426 };
37427 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
37428     fly: Roo.Element.fly,
37429
37430     b4StartDrag : function(x, y){
37431         this.view.headersDisabled = true;
37432         this.proxy.setHeight(this.view.mainWrap.getHeight());
37433         var w = this.cm.getColumnWidth(this.cellIndex);
37434         var minw = Math.max(w-this.grid.minColumnWidth, 0);
37435         this.resetConstraints();
37436         this.setXConstraint(minw, 1000);
37437         this.setYConstraint(0, 0);
37438         this.minX = x - minw;
37439         this.maxX = x + 1000;
37440         this.startPos = x;
37441         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
37442     },
37443
37444
37445     handleMouseDown : function(e){
37446         ev = Roo.EventObject.setEvent(e);
37447         var t = this.fly(ev.getTarget());
37448         if(t.hasClass("x-grid-split")){
37449             this.cellIndex = this.view.getCellIndex(t.dom);
37450             this.split = t.dom;
37451             this.cm = this.grid.colModel;
37452             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
37453                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
37454             }
37455         }
37456     },
37457
37458     endDrag : function(e){
37459         this.view.headersDisabled = false;
37460         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
37461         var diff = endX - this.startPos;
37462         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
37463     },
37464
37465     autoOffset : function(){
37466         this.setDelta(0,0);
37467     }
37468 });/*
37469  * Based on:
37470  * Ext JS Library 1.1.1
37471  * Copyright(c) 2006-2007, Ext JS, LLC.
37472  *
37473  * Originally Released Under LGPL - original licence link has changed is not relivant.
37474  *
37475  * Fork - LGPL
37476  * <script type="text/javascript">
37477  */
37478  
37479 // private
37480 // This is a support class used internally by the Grid components
37481 Roo.grid.GridDragZone = function(grid, config){
37482     this.view = grid.getView();
37483     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
37484     if(this.view.lockedBody){
37485         this.setHandleElId(Roo.id(this.view.mainBody.dom));
37486         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
37487     }
37488     this.scroll = false;
37489     this.grid = grid;
37490     this.ddel = document.createElement('div');
37491     this.ddel.className = 'x-grid-dd-wrap';
37492 };
37493
37494 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
37495     ddGroup : "GridDD",
37496
37497     getDragData : function(e){
37498         var t = Roo.lib.Event.getTarget(e);
37499         var rowIndex = this.view.findRowIndex(t);
37500         var sm = this.grid.selModel;
37501             
37502         //Roo.log(rowIndex);
37503         
37504         if (sm.getSelectedCell) {
37505             // cell selection..
37506             if (!sm.getSelectedCell()) {
37507                 return false;
37508             }
37509             if (rowIndex != sm.getSelectedCell()[0]) {
37510                 return false;
37511             }
37512         
37513         }
37514         
37515         if(rowIndex !== false){
37516             
37517             // if editorgrid.. 
37518             
37519             
37520             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
37521                
37522             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
37523               //  
37524             //}
37525             if (e.hasModifier()){
37526                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
37527             }
37528             
37529             Roo.log("getDragData");
37530             
37531             return {
37532                 grid: this.grid,
37533                 ddel: this.ddel,
37534                 rowIndex: rowIndex,
37535                 selections:sm.getSelections ? sm.getSelections() : (
37536                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
37537                 )
37538             };
37539         }
37540         return false;
37541     },
37542
37543     onInitDrag : function(e){
37544         var data = this.dragData;
37545         this.ddel.innerHTML = this.grid.getDragDropText();
37546         this.proxy.update(this.ddel);
37547         // fire start drag?
37548     },
37549
37550     afterRepair : function(){
37551         this.dragging = false;
37552     },
37553
37554     getRepairXY : function(e, data){
37555         return false;
37556     },
37557
37558     onEndDrag : function(data, e){
37559         // fire end drag?
37560     },
37561
37562     onValidDrop : function(dd, e, id){
37563         // fire drag drop?
37564         this.hideProxy();
37565     },
37566
37567     beforeInvalidDrop : function(e, id){
37568
37569     }
37570 });/*
37571  * Based on:
37572  * Ext JS Library 1.1.1
37573  * Copyright(c) 2006-2007, Ext JS, LLC.
37574  *
37575  * Originally Released Under LGPL - original licence link has changed is not relivant.
37576  *
37577  * Fork - LGPL
37578  * <script type="text/javascript">
37579  */
37580  
37581
37582 /**
37583  * @class Roo.grid.ColumnModel
37584  * @extends Roo.util.Observable
37585  * This is the default implementation of a ColumnModel used by the Grid. It defines
37586  * the columns in the grid.
37587  * <br>Usage:<br>
37588  <pre><code>
37589  var colModel = new Roo.grid.ColumnModel([
37590         {header: "Ticker", width: 60, sortable: true, locked: true},
37591         {header: "Company Name", width: 150, sortable: true},
37592         {header: "Market Cap.", width: 100, sortable: true},
37593         {header: "$ Sales", width: 100, sortable: true, renderer: money},
37594         {header: "Employees", width: 100, sortable: true, resizable: false}
37595  ]);
37596  </code></pre>
37597  * <p>
37598  
37599  * The config options listed for this class are options which may appear in each
37600  * individual column definition.
37601  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
37602  * @constructor
37603  * @param {Object} config An Array of column config objects. See this class's
37604  * config objects for details.
37605 */
37606 Roo.grid.ColumnModel = function(config){
37607         /**
37608      * The config passed into the constructor
37609      */
37610     this.config = config;
37611     this.lookup = {};
37612
37613     // if no id, create one
37614     // if the column does not have a dataIndex mapping,
37615     // map it to the order it is in the config
37616     for(var i = 0, len = config.length; i < len; i++){
37617         var c = config[i];
37618         if(typeof c.dataIndex == "undefined"){
37619             c.dataIndex = i;
37620         }
37621         if(typeof c.renderer == "string"){
37622             c.renderer = Roo.util.Format[c.renderer];
37623         }
37624         if(typeof c.id == "undefined"){
37625             c.id = Roo.id();
37626         }
37627         if(c.editor && c.editor.xtype){
37628             c.editor  = Roo.factory(c.editor, Roo.grid);
37629         }
37630         if(c.editor && c.editor.isFormField){
37631             c.editor = new Roo.grid.GridEditor(c.editor);
37632         }
37633         this.lookup[c.id] = c;
37634     }
37635
37636     /**
37637      * The width of columns which have no width specified (defaults to 100)
37638      * @type Number
37639      */
37640     this.defaultWidth = 100;
37641
37642     /**
37643      * Default sortable of columns which have no sortable specified (defaults to false)
37644      * @type Boolean
37645      */
37646     this.defaultSortable = false;
37647
37648     this.addEvents({
37649         /**
37650              * @event widthchange
37651              * Fires when the width of a column changes.
37652              * @param {ColumnModel} this
37653              * @param {Number} columnIndex The column index
37654              * @param {Number} newWidth The new width
37655              */
37656             "widthchange": true,
37657         /**
37658              * @event headerchange
37659              * Fires when the text of a header changes.
37660              * @param {ColumnModel} this
37661              * @param {Number} columnIndex The column index
37662              * @param {Number} newText The new header text
37663              */
37664             "headerchange": true,
37665         /**
37666              * @event hiddenchange
37667              * Fires when a column is hidden or "unhidden".
37668              * @param {ColumnModel} this
37669              * @param {Number} columnIndex The column index
37670              * @param {Boolean} hidden true if hidden, false otherwise
37671              */
37672             "hiddenchange": true,
37673             /**
37674          * @event columnmoved
37675          * Fires when a column is moved.
37676          * @param {ColumnModel} this
37677          * @param {Number} oldIndex
37678          * @param {Number} newIndex
37679          */
37680         "columnmoved" : true,
37681         /**
37682          * @event columlockchange
37683          * Fires when a column's locked state is changed
37684          * @param {ColumnModel} this
37685          * @param {Number} colIndex
37686          * @param {Boolean} locked true if locked
37687          */
37688         "columnlockchange" : true
37689     });
37690     Roo.grid.ColumnModel.superclass.constructor.call(this);
37691 };
37692 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
37693     /**
37694      * @cfg {String} header The header text to display in the Grid view.
37695      */
37696     /**
37697      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
37698      * {@link Roo.data.Record} definition from which to draw the column's value. If not
37699      * specified, the column's index is used as an index into the Record's data Array.
37700      */
37701     /**
37702      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
37703      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
37704      */
37705     /**
37706      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
37707      * Defaults to the value of the {@link #defaultSortable} property.
37708      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
37709      */
37710     /**
37711      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
37712      */
37713     /**
37714      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
37715      */
37716     /**
37717      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
37718      */
37719     /**
37720      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
37721      */
37722     /**
37723      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
37724      * given the cell's data value. See {@link #setRenderer}. If not specified, the
37725      * default renderer uses the raw data value.
37726      */
37727        /**
37728      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
37729      */
37730     /**
37731      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
37732      */
37733
37734     /**
37735      * Returns the id of the column at the specified index.
37736      * @param {Number} index The column index
37737      * @return {String} the id
37738      */
37739     getColumnId : function(index){
37740         return this.config[index].id;
37741     },
37742
37743     /**
37744      * Returns the column for a specified id.
37745      * @param {String} id The column id
37746      * @return {Object} the column
37747      */
37748     getColumnById : function(id){
37749         return this.lookup[id];
37750     },
37751
37752     
37753     /**
37754      * Returns the column for a specified dataIndex.
37755      * @param {String} dataIndex The column dataIndex
37756      * @return {Object|Boolean} the column or false if not found
37757      */
37758     getColumnByDataIndex: function(dataIndex){
37759         var index = this.findColumnIndex(dataIndex);
37760         return index > -1 ? this.config[index] : false;
37761     },
37762     
37763     /**
37764      * Returns the index for a specified column id.
37765      * @param {String} id The column id
37766      * @return {Number} the index, or -1 if not found
37767      */
37768     getIndexById : function(id){
37769         for(var i = 0, len = this.config.length; i < len; i++){
37770             if(this.config[i].id == id){
37771                 return i;
37772             }
37773         }
37774         return -1;
37775     },
37776     
37777     /**
37778      * Returns the index for a specified column dataIndex.
37779      * @param {String} dataIndex The column dataIndex
37780      * @return {Number} the index, or -1 if not found
37781      */
37782     
37783     findColumnIndex : function(dataIndex){
37784         for(var i = 0, len = this.config.length; i < len; i++){
37785             if(this.config[i].dataIndex == dataIndex){
37786                 return i;
37787             }
37788         }
37789         return -1;
37790     },
37791     
37792     
37793     moveColumn : function(oldIndex, newIndex){
37794         var c = this.config[oldIndex];
37795         this.config.splice(oldIndex, 1);
37796         this.config.splice(newIndex, 0, c);
37797         this.dataMap = null;
37798         this.fireEvent("columnmoved", this, oldIndex, newIndex);
37799     },
37800
37801     isLocked : function(colIndex){
37802         return this.config[colIndex].locked === true;
37803     },
37804
37805     setLocked : function(colIndex, value, suppressEvent){
37806         if(this.isLocked(colIndex) == value){
37807             return;
37808         }
37809         this.config[colIndex].locked = value;
37810         if(!suppressEvent){
37811             this.fireEvent("columnlockchange", this, colIndex, value);
37812         }
37813     },
37814
37815     getTotalLockedWidth : function(){
37816         var totalWidth = 0;
37817         for(var i = 0; i < this.config.length; i++){
37818             if(this.isLocked(i) && !this.isHidden(i)){
37819                 this.totalWidth += this.getColumnWidth(i);
37820             }
37821         }
37822         return totalWidth;
37823     },
37824
37825     getLockedCount : function(){
37826         for(var i = 0, len = this.config.length; i < len; i++){
37827             if(!this.isLocked(i)){
37828                 return i;
37829             }
37830         }
37831     },
37832
37833     /**
37834      * Returns the number of columns.
37835      * @return {Number}
37836      */
37837     getColumnCount : function(visibleOnly){
37838         if(visibleOnly === true){
37839             var c = 0;
37840             for(var i = 0, len = this.config.length; i < len; i++){
37841                 if(!this.isHidden(i)){
37842                     c++;
37843                 }
37844             }
37845             return c;
37846         }
37847         return this.config.length;
37848     },
37849
37850     /**
37851      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
37852      * @param {Function} fn
37853      * @param {Object} scope (optional)
37854      * @return {Array} result
37855      */
37856     getColumnsBy : function(fn, scope){
37857         var r = [];
37858         for(var i = 0, len = this.config.length; i < len; i++){
37859             var c = this.config[i];
37860             if(fn.call(scope||this, c, i) === true){
37861                 r[r.length] = c;
37862             }
37863         }
37864         return r;
37865     },
37866
37867     /**
37868      * Returns true if the specified column is sortable.
37869      * @param {Number} col The column index
37870      * @return {Boolean}
37871      */
37872     isSortable : function(col){
37873         if(typeof this.config[col].sortable == "undefined"){
37874             return this.defaultSortable;
37875         }
37876         return this.config[col].sortable;
37877     },
37878
37879     /**
37880      * Returns the rendering (formatting) function defined for the column.
37881      * @param {Number} col The column index.
37882      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
37883      */
37884     getRenderer : function(col){
37885         if(!this.config[col].renderer){
37886             return Roo.grid.ColumnModel.defaultRenderer;
37887         }
37888         return this.config[col].renderer;
37889     },
37890
37891     /**
37892      * Sets the rendering (formatting) function for a column.
37893      * @param {Number} col The column index
37894      * @param {Function} fn The function to use to process the cell's raw data
37895      * to return HTML markup for the grid view. The render function is called with
37896      * the following parameters:<ul>
37897      * <li>Data value.</li>
37898      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
37899      * <li>css A CSS style string to apply to the table cell.</li>
37900      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
37901      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
37902      * <li>Row index</li>
37903      * <li>Column index</li>
37904      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
37905      */
37906     setRenderer : function(col, fn){
37907         this.config[col].renderer = fn;
37908     },
37909
37910     /**
37911      * Returns the width for the specified column.
37912      * @param {Number} col The column index
37913      * @return {Number}
37914      */
37915     getColumnWidth : function(col){
37916         return this.config[col].width * 1 || this.defaultWidth;
37917     },
37918
37919     /**
37920      * Sets the width for a column.
37921      * @param {Number} col The column index
37922      * @param {Number} width The new width
37923      */
37924     setColumnWidth : function(col, width, suppressEvent){
37925         this.config[col].width = width;
37926         this.totalWidth = null;
37927         if(!suppressEvent){
37928              this.fireEvent("widthchange", this, col, width);
37929         }
37930     },
37931
37932     /**
37933      * Returns the total width of all columns.
37934      * @param {Boolean} includeHidden True to include hidden column widths
37935      * @return {Number}
37936      */
37937     getTotalWidth : function(includeHidden){
37938         if(!this.totalWidth){
37939             this.totalWidth = 0;
37940             for(var i = 0, len = this.config.length; i < len; i++){
37941                 if(includeHidden || !this.isHidden(i)){
37942                     this.totalWidth += this.getColumnWidth(i);
37943                 }
37944             }
37945         }
37946         return this.totalWidth;
37947     },
37948
37949     /**
37950      * Returns the header for the specified column.
37951      * @param {Number} col The column index
37952      * @return {String}
37953      */
37954     getColumnHeader : function(col){
37955         return this.config[col].header;
37956     },
37957
37958     /**
37959      * Sets the header for a column.
37960      * @param {Number} col The column index
37961      * @param {String} header The new header
37962      */
37963     setColumnHeader : function(col, header){
37964         this.config[col].header = header;
37965         this.fireEvent("headerchange", this, col, header);
37966     },
37967
37968     /**
37969      * Returns the tooltip for the specified column.
37970      * @param {Number} col The column index
37971      * @return {String}
37972      */
37973     getColumnTooltip : function(col){
37974             return this.config[col].tooltip;
37975     },
37976     /**
37977      * Sets the tooltip for a column.
37978      * @param {Number} col The column index
37979      * @param {String} tooltip The new tooltip
37980      */
37981     setColumnTooltip : function(col, tooltip){
37982             this.config[col].tooltip = tooltip;
37983     },
37984
37985     /**
37986      * Returns the dataIndex for the specified column.
37987      * @param {Number} col The column index
37988      * @return {Number}
37989      */
37990     getDataIndex : function(col){
37991         return this.config[col].dataIndex;
37992     },
37993
37994     /**
37995      * Sets the dataIndex for a column.
37996      * @param {Number} col The column index
37997      * @param {Number} dataIndex The new dataIndex
37998      */
37999     setDataIndex : function(col, dataIndex){
38000         this.config[col].dataIndex = dataIndex;
38001     },
38002
38003     
38004     
38005     /**
38006      * Returns true if the cell is editable.
38007      * @param {Number} colIndex The column index
38008      * @param {Number} rowIndex The row index
38009      * @return {Boolean}
38010      */
38011     isCellEditable : function(colIndex, rowIndex){
38012         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
38013     },
38014
38015     /**
38016      * Returns the editor defined for the cell/column.
38017      * return false or null to disable editing.
38018      * @param {Number} colIndex The column index
38019      * @param {Number} rowIndex The row index
38020      * @return {Object}
38021      */
38022     getCellEditor : function(colIndex, rowIndex){
38023         return this.config[colIndex].editor;
38024     },
38025
38026     /**
38027      * Sets if a column is editable.
38028      * @param {Number} col The column index
38029      * @param {Boolean} editable True if the column is editable
38030      */
38031     setEditable : function(col, editable){
38032         this.config[col].editable = editable;
38033     },
38034
38035
38036     /**
38037      * Returns true if the column is hidden.
38038      * @param {Number} colIndex The column index
38039      * @return {Boolean}
38040      */
38041     isHidden : function(colIndex){
38042         return this.config[colIndex].hidden;
38043     },
38044
38045
38046     /**
38047      * Returns true if the column width cannot be changed
38048      */
38049     isFixed : function(colIndex){
38050         return this.config[colIndex].fixed;
38051     },
38052
38053     /**
38054      * Returns true if the column can be resized
38055      * @return {Boolean}
38056      */
38057     isResizable : function(colIndex){
38058         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
38059     },
38060     /**
38061      * Sets if a column is hidden.
38062      * @param {Number} colIndex The column index
38063      * @param {Boolean} hidden True if the column is hidden
38064      */
38065     setHidden : function(colIndex, hidden){
38066         this.config[colIndex].hidden = hidden;
38067         this.totalWidth = null;
38068         this.fireEvent("hiddenchange", this, colIndex, hidden);
38069     },
38070
38071     /**
38072      * Sets the editor for a column.
38073      * @param {Number} col The column index
38074      * @param {Object} editor The editor object
38075      */
38076     setEditor : function(col, editor){
38077         this.config[col].editor = editor;
38078     }
38079 });
38080
38081 Roo.grid.ColumnModel.defaultRenderer = function(value){
38082         if(typeof value == "string" && value.length < 1){
38083             return "&#160;";
38084         }
38085         return value;
38086 };
38087
38088 // Alias for backwards compatibility
38089 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
38090 /*
38091  * Based on:
38092  * Ext JS Library 1.1.1
38093  * Copyright(c) 2006-2007, Ext JS, LLC.
38094  *
38095  * Originally Released Under LGPL - original licence link has changed is not relivant.
38096  *
38097  * Fork - LGPL
38098  * <script type="text/javascript">
38099  */
38100
38101 /**
38102  * @class Roo.grid.AbstractSelectionModel
38103  * @extends Roo.util.Observable
38104  * Abstract base class for grid SelectionModels.  It provides the interface that should be
38105  * implemented by descendant classes.  This class should not be directly instantiated.
38106  * @constructor
38107  */
38108 Roo.grid.AbstractSelectionModel = function(){
38109     this.locked = false;
38110     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
38111 };
38112
38113 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
38114     /** @ignore Called by the grid automatically. Do not call directly. */
38115     init : function(grid){
38116         this.grid = grid;
38117         this.initEvents();
38118     },
38119
38120     /**
38121      * Locks the selections.
38122      */
38123     lock : function(){
38124         this.locked = true;
38125     },
38126
38127     /**
38128      * Unlocks the selections.
38129      */
38130     unlock : function(){
38131         this.locked = false;
38132     },
38133
38134     /**
38135      * Returns true if the selections are locked.
38136      * @return {Boolean}
38137      */
38138     isLocked : function(){
38139         return this.locked;
38140     }
38141 });/*
38142  * Based on:
38143  * Ext JS Library 1.1.1
38144  * Copyright(c) 2006-2007, Ext JS, LLC.
38145  *
38146  * Originally Released Under LGPL - original licence link has changed is not relivant.
38147  *
38148  * Fork - LGPL
38149  * <script type="text/javascript">
38150  */
38151 /**
38152  * @extends Roo.grid.AbstractSelectionModel
38153  * @class Roo.grid.RowSelectionModel
38154  * The default SelectionModel used by {@link Roo.grid.Grid}.
38155  * It supports multiple selections and keyboard selection/navigation. 
38156  * @constructor
38157  * @param {Object} config
38158  */
38159 Roo.grid.RowSelectionModel = function(config){
38160     Roo.apply(this, config);
38161     this.selections = new Roo.util.MixedCollection(false, function(o){
38162         return o.id;
38163     });
38164
38165     this.last = false;
38166     this.lastActive = false;
38167
38168     this.addEvents({
38169         /**
38170              * @event selectionchange
38171              * Fires when the selection changes
38172              * @param {SelectionModel} this
38173              */
38174             "selectionchange" : true,
38175         /**
38176              * @event afterselectionchange
38177              * Fires after the selection changes (eg. by key press or clicking)
38178              * @param {SelectionModel} this
38179              */
38180             "afterselectionchange" : true,
38181         /**
38182              * @event beforerowselect
38183              * Fires when a row is selected being selected, return false to cancel.
38184              * @param {SelectionModel} this
38185              * @param {Number} rowIndex The selected index
38186              * @param {Boolean} keepExisting False if other selections will be cleared
38187              */
38188             "beforerowselect" : true,
38189         /**
38190              * @event rowselect
38191              * Fires when a row is selected.
38192              * @param {SelectionModel} this
38193              * @param {Number} rowIndex The selected index
38194              * @param {Roo.data.Record} r The record
38195              */
38196             "rowselect" : true,
38197         /**
38198              * @event rowdeselect
38199              * Fires when a row is deselected.
38200              * @param {SelectionModel} this
38201              * @param {Number} rowIndex The selected index
38202              */
38203         "rowdeselect" : true
38204     });
38205     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
38206     this.locked = false;
38207 };
38208
38209 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
38210     /**
38211      * @cfg {Boolean} singleSelect
38212      * True to allow selection of only one row at a time (defaults to false)
38213      */
38214     singleSelect : false,
38215
38216     // private
38217     initEvents : function(){
38218
38219         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
38220             this.grid.on("mousedown", this.handleMouseDown, this);
38221         }else{ // allow click to work like normal
38222             this.grid.on("rowclick", this.handleDragableRowClick, this);
38223         }
38224
38225         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
38226             "up" : function(e){
38227                 if(!e.shiftKey){
38228                     this.selectPrevious(e.shiftKey);
38229                 }else if(this.last !== false && this.lastActive !== false){
38230                     var last = this.last;
38231                     this.selectRange(this.last,  this.lastActive-1);
38232                     this.grid.getView().focusRow(this.lastActive);
38233                     if(last !== false){
38234                         this.last = last;
38235                     }
38236                 }else{
38237                     this.selectFirstRow();
38238                 }
38239                 this.fireEvent("afterselectionchange", this);
38240             },
38241             "down" : function(e){
38242                 if(!e.shiftKey){
38243                     this.selectNext(e.shiftKey);
38244                 }else if(this.last !== false && this.lastActive !== false){
38245                     var last = this.last;
38246                     this.selectRange(this.last,  this.lastActive+1);
38247                     this.grid.getView().focusRow(this.lastActive);
38248                     if(last !== false){
38249                         this.last = last;
38250                     }
38251                 }else{
38252                     this.selectFirstRow();
38253                 }
38254                 this.fireEvent("afterselectionchange", this);
38255             },
38256             scope: this
38257         });
38258
38259         var view = this.grid.view;
38260         view.on("refresh", this.onRefresh, this);
38261         view.on("rowupdated", this.onRowUpdated, this);
38262         view.on("rowremoved", this.onRemove, this);
38263     },
38264
38265     // private
38266     onRefresh : function(){
38267         var ds = this.grid.dataSource, i, v = this.grid.view;
38268         var s = this.selections;
38269         s.each(function(r){
38270             if((i = ds.indexOfId(r.id)) != -1){
38271                 v.onRowSelect(i);
38272             }else{
38273                 s.remove(r);
38274             }
38275         });
38276     },
38277
38278     // private
38279     onRemove : function(v, index, r){
38280         this.selections.remove(r);
38281     },
38282
38283     // private
38284     onRowUpdated : function(v, index, r){
38285         if(this.isSelected(r)){
38286             v.onRowSelect(index);
38287         }
38288     },
38289
38290     /**
38291      * Select records.
38292      * @param {Array} records The records to select
38293      * @param {Boolean} keepExisting (optional) True to keep existing selections
38294      */
38295     selectRecords : function(records, keepExisting){
38296         if(!keepExisting){
38297             this.clearSelections();
38298         }
38299         var ds = this.grid.dataSource;
38300         for(var i = 0, len = records.length; i < len; i++){
38301             this.selectRow(ds.indexOf(records[i]), true);
38302         }
38303     },
38304
38305     /**
38306      * Gets the number of selected rows.
38307      * @return {Number}
38308      */
38309     getCount : function(){
38310         return this.selections.length;
38311     },
38312
38313     /**
38314      * Selects the first row in the grid.
38315      */
38316     selectFirstRow : function(){
38317         this.selectRow(0);
38318     },
38319
38320     /**
38321      * Select the last row.
38322      * @param {Boolean} keepExisting (optional) True to keep existing selections
38323      */
38324     selectLastRow : function(keepExisting){
38325         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
38326     },
38327
38328     /**
38329      * Selects the row immediately following the last selected row.
38330      * @param {Boolean} keepExisting (optional) True to keep existing selections
38331      */
38332     selectNext : function(keepExisting){
38333         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
38334             this.selectRow(this.last+1, keepExisting);
38335             this.grid.getView().focusRow(this.last);
38336         }
38337     },
38338
38339     /**
38340      * Selects the row that precedes the last selected row.
38341      * @param {Boolean} keepExisting (optional) True to keep existing selections
38342      */
38343     selectPrevious : function(keepExisting){
38344         if(this.last){
38345             this.selectRow(this.last-1, keepExisting);
38346             this.grid.getView().focusRow(this.last);
38347         }
38348     },
38349
38350     /**
38351      * Returns the selected records
38352      * @return {Array} Array of selected records
38353      */
38354     getSelections : function(){
38355         return [].concat(this.selections.items);
38356     },
38357
38358     /**
38359      * Returns the first selected record.
38360      * @return {Record}
38361      */
38362     getSelected : function(){
38363         return this.selections.itemAt(0);
38364     },
38365
38366
38367     /**
38368      * Clears all selections.
38369      */
38370     clearSelections : function(fast){
38371         if(this.locked) return;
38372         if(fast !== true){
38373             var ds = this.grid.dataSource;
38374             var s = this.selections;
38375             s.each(function(r){
38376                 this.deselectRow(ds.indexOfId(r.id));
38377             }, this);
38378             s.clear();
38379         }else{
38380             this.selections.clear();
38381         }
38382         this.last = false;
38383     },
38384
38385
38386     /**
38387      * Selects all rows.
38388      */
38389     selectAll : function(){
38390         if(this.locked) return;
38391         this.selections.clear();
38392         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
38393             this.selectRow(i, true);
38394         }
38395     },
38396
38397     /**
38398      * Returns True if there is a selection.
38399      * @return {Boolean}
38400      */
38401     hasSelection : function(){
38402         return this.selections.length > 0;
38403     },
38404
38405     /**
38406      * Returns True if the specified row is selected.
38407      * @param {Number/Record} record The record or index of the record to check
38408      * @return {Boolean}
38409      */
38410     isSelected : function(index){
38411         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
38412         return (r && this.selections.key(r.id) ? true : false);
38413     },
38414
38415     /**
38416      * Returns True if the specified record id is selected.
38417      * @param {String} id The id of record to check
38418      * @return {Boolean}
38419      */
38420     isIdSelected : function(id){
38421         return (this.selections.key(id) ? true : false);
38422     },
38423
38424     // private
38425     handleMouseDown : function(e, t){
38426         var view = this.grid.getView(), rowIndex;
38427         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
38428             return;
38429         };
38430         if(e.shiftKey && this.last !== false){
38431             var last = this.last;
38432             this.selectRange(last, rowIndex, e.ctrlKey);
38433             this.last = last; // reset the last
38434             view.focusRow(rowIndex);
38435         }else{
38436             var isSelected = this.isSelected(rowIndex);
38437             if(e.button !== 0 && isSelected){
38438                 view.focusRow(rowIndex);
38439             }else if(e.ctrlKey && isSelected){
38440                 this.deselectRow(rowIndex);
38441             }else if(!isSelected){
38442                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
38443                 view.focusRow(rowIndex);
38444             }
38445         }
38446         this.fireEvent("afterselectionchange", this);
38447     },
38448     // private
38449     handleDragableRowClick :  function(grid, rowIndex, e) 
38450     {
38451         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
38452             this.selectRow(rowIndex, false);
38453             grid.view.focusRow(rowIndex);
38454              this.fireEvent("afterselectionchange", this);
38455         }
38456     },
38457     
38458     /**
38459      * Selects multiple rows.
38460      * @param {Array} rows Array of the indexes of the row to select
38461      * @param {Boolean} keepExisting (optional) True to keep existing selections
38462      */
38463     selectRows : function(rows, keepExisting){
38464         if(!keepExisting){
38465             this.clearSelections();
38466         }
38467         for(var i = 0, len = rows.length; i < len; i++){
38468             this.selectRow(rows[i], true);
38469         }
38470     },
38471
38472     /**
38473      * Selects a range of rows. All rows in between startRow and endRow are also selected.
38474      * @param {Number} startRow The index of the first row in the range
38475      * @param {Number} endRow The index of the last row in the range
38476      * @param {Boolean} keepExisting (optional) True to retain existing selections
38477      */
38478     selectRange : function(startRow, endRow, keepExisting){
38479         if(this.locked) return;
38480         if(!keepExisting){
38481             this.clearSelections();
38482         }
38483         if(startRow <= endRow){
38484             for(var i = startRow; i <= endRow; i++){
38485                 this.selectRow(i, true);
38486             }
38487         }else{
38488             for(var i = startRow; i >= endRow; i--){
38489                 this.selectRow(i, true);
38490             }
38491         }
38492     },
38493
38494     /**
38495      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
38496      * @param {Number} startRow The index of the first row in the range
38497      * @param {Number} endRow The index of the last row in the range
38498      */
38499     deselectRange : function(startRow, endRow, preventViewNotify){
38500         if(this.locked) return;
38501         for(var i = startRow; i <= endRow; i++){
38502             this.deselectRow(i, preventViewNotify);
38503         }
38504     },
38505
38506     /**
38507      * Selects a row.
38508      * @param {Number} row The index of the row to select
38509      * @param {Boolean} keepExisting (optional) True to keep existing selections
38510      */
38511     selectRow : function(index, keepExisting, preventViewNotify){
38512         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
38513         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
38514             if(!keepExisting || this.singleSelect){
38515                 this.clearSelections();
38516             }
38517             var r = this.grid.dataSource.getAt(index);
38518             this.selections.add(r);
38519             this.last = this.lastActive = index;
38520             if(!preventViewNotify){
38521                 this.grid.getView().onRowSelect(index);
38522             }
38523             this.fireEvent("rowselect", this, index, r);
38524             this.fireEvent("selectionchange", this);
38525         }
38526     },
38527
38528     /**
38529      * Deselects a row.
38530      * @param {Number} row The index of the row to deselect
38531      */
38532     deselectRow : function(index, preventViewNotify){
38533         if(this.locked) return;
38534         if(this.last == index){
38535             this.last = false;
38536         }
38537         if(this.lastActive == index){
38538             this.lastActive = false;
38539         }
38540         var r = this.grid.dataSource.getAt(index);
38541         this.selections.remove(r);
38542         if(!preventViewNotify){
38543             this.grid.getView().onRowDeselect(index);
38544         }
38545         this.fireEvent("rowdeselect", this, index);
38546         this.fireEvent("selectionchange", this);
38547     },
38548
38549     // private
38550     restoreLast : function(){
38551         if(this._last){
38552             this.last = this._last;
38553         }
38554     },
38555
38556     // private
38557     acceptsNav : function(row, col, cm){
38558         return !cm.isHidden(col) && cm.isCellEditable(col, row);
38559     },
38560
38561     // private
38562     onEditorKey : function(field, e){
38563         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
38564         if(k == e.TAB){
38565             e.stopEvent();
38566             ed.completeEdit();
38567             if(e.shiftKey){
38568                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
38569             }else{
38570                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
38571             }
38572         }else if(k == e.ENTER && !e.ctrlKey){
38573             e.stopEvent();
38574             ed.completeEdit();
38575             if(e.shiftKey){
38576                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
38577             }else{
38578                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
38579             }
38580         }else if(k == e.ESC){
38581             ed.cancelEdit();
38582         }
38583         if(newCell){
38584             g.startEditing(newCell[0], newCell[1]);
38585         }
38586     }
38587 });/*
38588  * Based on:
38589  * Ext JS Library 1.1.1
38590  * Copyright(c) 2006-2007, Ext JS, LLC.
38591  *
38592  * Originally Released Under LGPL - original licence link has changed is not relivant.
38593  *
38594  * Fork - LGPL
38595  * <script type="text/javascript">
38596  */
38597 /**
38598  * @class Roo.grid.CellSelectionModel
38599  * @extends Roo.grid.AbstractSelectionModel
38600  * This class provides the basic implementation for cell selection in a grid.
38601  * @constructor
38602  * @param {Object} config The object containing the configuration of this model.
38603  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
38604  */
38605 Roo.grid.CellSelectionModel = function(config){
38606     Roo.apply(this, config);
38607
38608     this.selection = null;
38609
38610     this.addEvents({
38611         /**
38612              * @event beforerowselect
38613              * Fires before a cell is selected.
38614              * @param {SelectionModel} this
38615              * @param {Number} rowIndex The selected row index
38616              * @param {Number} colIndex The selected cell index
38617              */
38618             "beforecellselect" : true,
38619         /**
38620              * @event cellselect
38621              * Fires when a cell is selected.
38622              * @param {SelectionModel} this
38623              * @param {Number} rowIndex The selected row index
38624              * @param {Number} colIndex The selected cell index
38625              */
38626             "cellselect" : true,
38627         /**
38628              * @event selectionchange
38629              * Fires when the active selection changes.
38630              * @param {SelectionModel} this
38631              * @param {Object} selection null for no selection or an object (o) with two properties
38632                 <ul>
38633                 <li>o.record: the record object for the row the selection is in</li>
38634                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
38635                 </ul>
38636              */
38637             "selectionchange" : true,
38638         /**
38639              * @event tabend
38640              * Fires when the tab (or enter) was pressed on the last editable cell
38641              * You can use this to trigger add new row.
38642              * @param {SelectionModel} this
38643              */
38644             "tabend" : true,
38645          /**
38646              * @event beforeeditnext
38647              * Fires before the next editable sell is made active
38648              * You can use this to skip to another cell or fire the tabend
38649              *    if you set cell to false
38650              * @param {Object} eventdata object : { cell : [ row, col ] } 
38651              */
38652             "beforeeditnext" : true
38653     });
38654     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
38655 };
38656
38657 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
38658     
38659     enter_is_tab: false,
38660
38661     /** @ignore */
38662     initEvents : function(){
38663         this.grid.on("mousedown", this.handleMouseDown, this);
38664         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
38665         var view = this.grid.view;
38666         view.on("refresh", this.onViewChange, this);
38667         view.on("rowupdated", this.onRowUpdated, this);
38668         view.on("beforerowremoved", this.clearSelections, this);
38669         view.on("beforerowsinserted", this.clearSelections, this);
38670         if(this.grid.isEditor){
38671             this.grid.on("beforeedit", this.beforeEdit,  this);
38672         }
38673     },
38674
38675         //private
38676     beforeEdit : function(e){
38677         this.select(e.row, e.column, false, true, e.record);
38678     },
38679
38680         //private
38681     onRowUpdated : function(v, index, r){
38682         if(this.selection && this.selection.record == r){
38683             v.onCellSelect(index, this.selection.cell[1]);
38684         }
38685     },
38686
38687         //private
38688     onViewChange : function(){
38689         this.clearSelections(true);
38690     },
38691
38692         /**
38693          * Returns the currently selected cell,.
38694          * @return {Array} The selected cell (row, column) or null if none selected.
38695          */
38696     getSelectedCell : function(){
38697         return this.selection ? this.selection.cell : null;
38698     },
38699
38700     /**
38701      * Clears all selections.
38702      * @param {Boolean} true to prevent the gridview from being notified about the change.
38703      */
38704     clearSelections : function(preventNotify){
38705         var s = this.selection;
38706         if(s){
38707             if(preventNotify !== true){
38708                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
38709             }
38710             this.selection = null;
38711             this.fireEvent("selectionchange", this, null);
38712         }
38713     },
38714
38715     /**
38716      * Returns true if there is a selection.
38717      * @return {Boolean}
38718      */
38719     hasSelection : function(){
38720         return this.selection ? true : false;
38721     },
38722
38723     /** @ignore */
38724     handleMouseDown : function(e, t){
38725         var v = this.grid.getView();
38726         if(this.isLocked()){
38727             return;
38728         };
38729         var row = v.findRowIndex(t);
38730         var cell = v.findCellIndex(t);
38731         if(row !== false && cell !== false){
38732             this.select(row, cell);
38733         }
38734     },
38735
38736     /**
38737      * Selects a cell.
38738      * @param {Number} rowIndex
38739      * @param {Number} collIndex
38740      */
38741     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
38742         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
38743             this.clearSelections();
38744             r = r || this.grid.dataSource.getAt(rowIndex);
38745             this.selection = {
38746                 record : r,
38747                 cell : [rowIndex, colIndex]
38748             };
38749             if(!preventViewNotify){
38750                 var v = this.grid.getView();
38751                 v.onCellSelect(rowIndex, colIndex);
38752                 if(preventFocus !== true){
38753                     v.focusCell(rowIndex, colIndex);
38754                 }
38755             }
38756             this.fireEvent("cellselect", this, rowIndex, colIndex);
38757             this.fireEvent("selectionchange", this, this.selection);
38758         }
38759     },
38760
38761         //private
38762     isSelectable : function(rowIndex, colIndex, cm){
38763         return !cm.isHidden(colIndex);
38764     },
38765
38766     /** @ignore */
38767     handleKeyDown : function(e){
38768         //Roo.log('Cell Sel Model handleKeyDown');
38769         if(!e.isNavKeyPress()){
38770             return;
38771         }
38772         var g = this.grid, s = this.selection;
38773         if(!s){
38774             e.stopEvent();
38775             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
38776             if(cell){
38777                 this.select(cell[0], cell[1]);
38778             }
38779             return;
38780         }
38781         var sm = this;
38782         var walk = function(row, col, step){
38783             return g.walkCells(row, col, step, sm.isSelectable,  sm);
38784         };
38785         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
38786         var newCell;
38787
38788       
38789
38790         switch(k){
38791             case e.TAB:
38792                 // handled by onEditorKey
38793                 if (g.isEditor && g.editing) {
38794                     return;
38795                 }
38796                 if(e.shiftKey) {
38797                     newCell = walk(r, c-1, -1);
38798                 } else {
38799                     newCell = walk(r, c+1, 1);
38800                 }
38801                 break;
38802             
38803             case e.DOWN:
38804                newCell = walk(r+1, c, 1);
38805                 break;
38806             
38807             case e.UP:
38808                 newCell = walk(r-1, c, -1);
38809                 break;
38810             
38811             case e.RIGHT:
38812                 newCell = walk(r, c+1, 1);
38813                 break;
38814             
38815             case e.LEFT:
38816                 newCell = walk(r, c-1, -1);
38817                 break;
38818             
38819             case e.ENTER:
38820                 
38821                 if(g.isEditor && !g.editing){
38822                    g.startEditing(r, c);
38823                    e.stopEvent();
38824                    return;
38825                 }
38826                 
38827                 
38828              break;
38829         };
38830         if(newCell){
38831             this.select(newCell[0], newCell[1]);
38832             e.stopEvent();
38833             
38834         }
38835     },
38836
38837     acceptsNav : function(row, col, cm){
38838         return !cm.isHidden(col) && cm.isCellEditable(col, row);
38839     },
38840     /**
38841      * Selects a cell.
38842      * @param {Number} field (not used) - as it's normally used as a listener
38843      * @param {Number} e - event - fake it by using
38844      *
38845      * var e = Roo.EventObjectImpl.prototype;
38846      * e.keyCode = e.TAB
38847      *
38848      * 
38849      */
38850     onEditorKey : function(field, e){
38851         
38852         var k = e.getKey(),
38853             newCell,
38854             g = this.grid,
38855             ed = g.activeEditor,
38856             forward = false;
38857         ///Roo.log('onEditorKey' + k);
38858         
38859         
38860         if (this.enter_is_tab && k == e.ENTER) {
38861             k = e.TAB;
38862         }
38863         
38864         if(k == e.TAB){
38865             if(e.shiftKey){
38866                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
38867             }else{
38868                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
38869                 forward = true;
38870             }
38871             
38872             e.stopEvent();
38873             
38874         } else if(k == e.ENTER &&  !e.ctrlKey){
38875             ed.completeEdit();
38876             e.stopEvent();
38877             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
38878         
38879                 } else if(k == e.ESC){
38880             ed.cancelEdit();
38881         }
38882                 
38883         if (newCell) {
38884             var ecall = { cell : newCell, forward : forward };
38885             this.fireEvent('beforeeditnext', ecall );
38886             newCell = ecall.cell;
38887                         forward = ecall.forward;
38888         }
38889                 
38890         if(newCell){
38891             //Roo.log('next cell after edit');
38892             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
38893         } else if (forward) {
38894             // tabbed past last
38895             this.fireEvent.defer(100, this, ['tabend',this]);
38896         }
38897     }
38898 });/*
38899  * Based on:
38900  * Ext JS Library 1.1.1
38901  * Copyright(c) 2006-2007, Ext JS, LLC.
38902  *
38903  * Originally Released Under LGPL - original licence link has changed is not relivant.
38904  *
38905  * Fork - LGPL
38906  * <script type="text/javascript">
38907  */
38908  
38909 /**
38910  * @class Roo.grid.EditorGrid
38911  * @extends Roo.grid.Grid
38912  * Class for creating and editable grid.
38913  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
38914  * The container MUST have some type of size defined for the grid to fill. The container will be 
38915  * automatically set to position relative if it isn't already.
38916  * @param {Object} dataSource The data model to bind to
38917  * @param {Object} colModel The column model with info about this grid's columns
38918  */
38919 Roo.grid.EditorGrid = function(container, config){
38920     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
38921     this.getGridEl().addClass("xedit-grid");
38922
38923     if(!this.selModel){
38924         this.selModel = new Roo.grid.CellSelectionModel();
38925     }
38926
38927     this.activeEditor = null;
38928
38929         this.addEvents({
38930             /**
38931              * @event beforeedit
38932              * Fires before cell editing is triggered. The edit event object has the following properties <br />
38933              * <ul style="padding:5px;padding-left:16px;">
38934              * <li>grid - This grid</li>
38935              * <li>record - The record being edited</li>
38936              * <li>field - The field name being edited</li>
38937              * <li>value - The value for the field being edited.</li>
38938              * <li>row - The grid row index</li>
38939              * <li>column - The grid column index</li>
38940              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
38941              * </ul>
38942              * @param {Object} e An edit event (see above for description)
38943              */
38944             "beforeedit" : true,
38945             /**
38946              * @event afteredit
38947              * Fires after a cell is edited. <br />
38948              * <ul style="padding:5px;padding-left:16px;">
38949              * <li>grid - This grid</li>
38950              * <li>record - The record being edited</li>
38951              * <li>field - The field name being edited</li>
38952              * <li>value - The value being set</li>
38953              * <li>originalValue - The original value for the field, before the edit.</li>
38954              * <li>row - The grid row index</li>
38955              * <li>column - The grid column index</li>
38956              * </ul>
38957              * @param {Object} e An edit event (see above for description)
38958              */
38959             "afteredit" : true,
38960             /**
38961              * @event validateedit
38962              * Fires after a cell is edited, but before the value is set in the record. 
38963          * You can use this to modify the value being set in the field, Return false
38964              * to cancel the change. The edit event object has the following properties <br />
38965              * <ul style="padding:5px;padding-left:16px;">
38966          * <li>editor - This editor</li>
38967              * <li>grid - This grid</li>
38968              * <li>record - The record being edited</li>
38969              * <li>field - The field name being edited</li>
38970              * <li>value - The value being set</li>
38971              * <li>originalValue - The original value for the field, before the edit.</li>
38972              * <li>row - The grid row index</li>
38973              * <li>column - The grid column index</li>
38974              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
38975              * </ul>
38976              * @param {Object} e An edit event (see above for description)
38977              */
38978             "validateedit" : true
38979         });
38980     this.on("bodyscroll", this.stopEditing,  this);
38981     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
38982 };
38983
38984 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
38985     /**
38986      * @cfg {Number} clicksToEdit
38987      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
38988      */
38989     clicksToEdit: 2,
38990
38991     // private
38992     isEditor : true,
38993     // private
38994     trackMouseOver: false, // causes very odd FF errors
38995
38996     onCellDblClick : function(g, row, col){
38997         this.startEditing(row, col);
38998     },
38999
39000     onEditComplete : function(ed, value, startValue){
39001         this.editing = false;
39002         this.activeEditor = null;
39003         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
39004         var r = ed.record;
39005         var field = this.colModel.getDataIndex(ed.col);
39006         var e = {
39007             grid: this,
39008             record: r,
39009             field: field,
39010             originalValue: startValue,
39011             value: value,
39012             row: ed.row,
39013             column: ed.col,
39014             cancel:false,
39015             editor: ed
39016         };
39017         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
39018         cell.show();
39019           
39020         if(String(value) !== String(startValue)){
39021             
39022             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
39023                 r.set(field, e.value);
39024                 // if we are dealing with a combo box..
39025                 // then we also set the 'name' colum to be the displayField
39026                 if (ed.field.displayField && ed.field.name) {
39027                     r.set(ed.field.name, ed.field.el.dom.value);
39028                 }
39029                 
39030                 delete e.cancel; //?? why!!!
39031                 this.fireEvent("afteredit", e);
39032             }
39033         } else {
39034             this.fireEvent("afteredit", e); // always fire it!
39035         }
39036         this.view.focusCell(ed.row, ed.col);
39037     },
39038
39039     /**
39040      * Starts editing the specified for the specified row/column
39041      * @param {Number} rowIndex
39042      * @param {Number} colIndex
39043      */
39044     startEditing : function(row, col){
39045         this.stopEditing();
39046         if(this.colModel.isCellEditable(col, row)){
39047             this.view.ensureVisible(row, col, true);
39048           
39049             var r = this.dataSource.getAt(row);
39050             var field = this.colModel.getDataIndex(col);
39051             var cell = Roo.get(this.view.getCell(row,col));
39052             var e = {
39053                 grid: this,
39054                 record: r,
39055                 field: field,
39056                 value: r.data[field],
39057                 row: row,
39058                 column: col,
39059                 cancel:false 
39060             };
39061             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
39062                 this.editing = true;
39063                 var ed = this.colModel.getCellEditor(col, row);
39064                 
39065                 if (!ed) {
39066                     return;
39067                 }
39068                 if(!ed.rendered){
39069                     ed.render(ed.parentEl || document.body);
39070                 }
39071                 ed.field.reset();
39072                
39073                 cell.hide();
39074                 
39075                 (function(){ // complex but required for focus issues in safari, ie and opera
39076                     ed.row = row;
39077                     ed.col = col;
39078                     ed.record = r;
39079                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
39080                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
39081                     this.activeEditor = ed;
39082                     var v = r.data[field];
39083                     ed.startEdit(this.view.getCell(row, col), v);
39084                     // combo's with 'displayField and name set
39085                     if (ed.field.displayField && ed.field.name) {
39086                         ed.field.el.dom.value = r.data[ed.field.name];
39087                     }
39088                     
39089                     
39090                 }).defer(50, this);
39091             }
39092         }
39093     },
39094         
39095     /**
39096      * Stops any active editing
39097      */
39098     stopEditing : function(){
39099         if(this.activeEditor){
39100             this.activeEditor.completeEdit();
39101         }
39102         this.activeEditor = null;
39103     },
39104         
39105          /**
39106      * Called to get grid's drag proxy text, by default returns this.ddText.
39107      * @return {String}
39108      */
39109     getDragDropText : function(){
39110         var count = this.selModel.getSelectedCell() ? 1 : 0;
39111         return String.format(this.ddText, count, count == 1 ? '' : 's');
39112     }
39113         
39114 });/*
39115  * Based on:
39116  * Ext JS Library 1.1.1
39117  * Copyright(c) 2006-2007, Ext JS, LLC.
39118  *
39119  * Originally Released Under LGPL - original licence link has changed is not relivant.
39120  *
39121  * Fork - LGPL
39122  * <script type="text/javascript">
39123  */
39124
39125 // private - not really -- you end up using it !
39126 // This is a support class used internally by the Grid components
39127
39128 /**
39129  * @class Roo.grid.GridEditor
39130  * @extends Roo.Editor
39131  * Class for creating and editable grid elements.
39132  * @param {Object} config any settings (must include field)
39133  */
39134 Roo.grid.GridEditor = function(field, config){
39135     if (!config && field.field) {
39136         config = field;
39137         field = Roo.factory(config.field, Roo.form);
39138     }
39139     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
39140     field.monitorTab = false;
39141 };
39142
39143 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
39144     
39145     /**
39146      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
39147      */
39148     
39149     alignment: "tl-tl",
39150     autoSize: "width",
39151     hideEl : false,
39152     cls: "x-small-editor x-grid-editor",
39153     shim:false,
39154     shadow:"frame"
39155 });/*
39156  * Based on:
39157  * Ext JS Library 1.1.1
39158  * Copyright(c) 2006-2007, Ext JS, LLC.
39159  *
39160  * Originally Released Under LGPL - original licence link has changed is not relivant.
39161  *
39162  * Fork - LGPL
39163  * <script type="text/javascript">
39164  */
39165   
39166
39167   
39168 Roo.grid.PropertyRecord = Roo.data.Record.create([
39169     {name:'name',type:'string'},  'value'
39170 ]);
39171
39172
39173 Roo.grid.PropertyStore = function(grid, source){
39174     this.grid = grid;
39175     this.store = new Roo.data.Store({
39176         recordType : Roo.grid.PropertyRecord
39177     });
39178     this.store.on('update', this.onUpdate,  this);
39179     if(source){
39180         this.setSource(source);
39181     }
39182     Roo.grid.PropertyStore.superclass.constructor.call(this);
39183 };
39184
39185
39186
39187 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
39188     setSource : function(o){
39189         this.source = o;
39190         this.store.removeAll();
39191         var data = [];
39192         for(var k in o){
39193             if(this.isEditableValue(o[k])){
39194                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
39195             }
39196         }
39197         this.store.loadRecords({records: data}, {}, true);
39198     },
39199
39200     onUpdate : function(ds, record, type){
39201         if(type == Roo.data.Record.EDIT){
39202             var v = record.data['value'];
39203             var oldValue = record.modified['value'];
39204             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
39205                 this.source[record.id] = v;
39206                 record.commit();
39207                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
39208             }else{
39209                 record.reject();
39210             }
39211         }
39212     },
39213
39214     getProperty : function(row){
39215        return this.store.getAt(row);
39216     },
39217
39218     isEditableValue: function(val){
39219         if(val && val instanceof Date){
39220             return true;
39221         }else if(typeof val == 'object' || typeof val == 'function'){
39222             return false;
39223         }
39224         return true;
39225     },
39226
39227     setValue : function(prop, value){
39228         this.source[prop] = value;
39229         this.store.getById(prop).set('value', value);
39230     },
39231
39232     getSource : function(){
39233         return this.source;
39234     }
39235 });
39236
39237 Roo.grid.PropertyColumnModel = function(grid, store){
39238     this.grid = grid;
39239     var g = Roo.grid;
39240     g.PropertyColumnModel.superclass.constructor.call(this, [
39241         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
39242         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
39243     ]);
39244     this.store = store;
39245     this.bselect = Roo.DomHelper.append(document.body, {
39246         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
39247             {tag: 'option', value: 'true', html: 'true'},
39248             {tag: 'option', value: 'false', html: 'false'}
39249         ]
39250     });
39251     Roo.id(this.bselect);
39252     var f = Roo.form;
39253     this.editors = {
39254         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
39255         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
39256         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
39257         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
39258         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
39259     };
39260     this.renderCellDelegate = this.renderCell.createDelegate(this);
39261     this.renderPropDelegate = this.renderProp.createDelegate(this);
39262 };
39263
39264 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
39265     
39266     
39267     nameText : 'Name',
39268     valueText : 'Value',
39269     
39270     dateFormat : 'm/j/Y',
39271     
39272     
39273     renderDate : function(dateVal){
39274         return dateVal.dateFormat(this.dateFormat);
39275     },
39276
39277     renderBool : function(bVal){
39278         return bVal ? 'true' : 'false';
39279     },
39280
39281     isCellEditable : function(colIndex, rowIndex){
39282         return colIndex == 1;
39283     },
39284
39285     getRenderer : function(col){
39286         return col == 1 ?
39287             this.renderCellDelegate : this.renderPropDelegate;
39288     },
39289
39290     renderProp : function(v){
39291         return this.getPropertyName(v);
39292     },
39293
39294     renderCell : function(val){
39295         var rv = val;
39296         if(val instanceof Date){
39297             rv = this.renderDate(val);
39298         }else if(typeof val == 'boolean'){
39299             rv = this.renderBool(val);
39300         }
39301         return Roo.util.Format.htmlEncode(rv);
39302     },
39303
39304     getPropertyName : function(name){
39305         var pn = this.grid.propertyNames;
39306         return pn && pn[name] ? pn[name] : name;
39307     },
39308
39309     getCellEditor : function(colIndex, rowIndex){
39310         var p = this.store.getProperty(rowIndex);
39311         var n = p.data['name'], val = p.data['value'];
39312         
39313         if(typeof(this.grid.customEditors[n]) == 'string'){
39314             return this.editors[this.grid.customEditors[n]];
39315         }
39316         if(typeof(this.grid.customEditors[n]) != 'undefined'){
39317             return this.grid.customEditors[n];
39318         }
39319         if(val instanceof Date){
39320             return this.editors['date'];
39321         }else if(typeof val == 'number'){
39322             return this.editors['number'];
39323         }else if(typeof val == 'boolean'){
39324             return this.editors['boolean'];
39325         }else{
39326             return this.editors['string'];
39327         }
39328     }
39329 });
39330
39331 /**
39332  * @class Roo.grid.PropertyGrid
39333  * @extends Roo.grid.EditorGrid
39334  * This class represents the  interface of a component based property grid control.
39335  * <br><br>Usage:<pre><code>
39336  var grid = new Roo.grid.PropertyGrid("my-container-id", {
39337       
39338  });
39339  // set any options
39340  grid.render();
39341  * </code></pre>
39342   
39343  * @constructor
39344  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
39345  * The container MUST have some type of size defined for the grid to fill. The container will be
39346  * automatically set to position relative if it isn't already.
39347  * @param {Object} config A config object that sets properties on this grid.
39348  */
39349 Roo.grid.PropertyGrid = function(container, config){
39350     config = config || {};
39351     var store = new Roo.grid.PropertyStore(this);
39352     this.store = store;
39353     var cm = new Roo.grid.PropertyColumnModel(this, store);
39354     store.store.sort('name', 'ASC');
39355     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
39356         ds: store.store,
39357         cm: cm,
39358         enableColLock:false,
39359         enableColumnMove:false,
39360         stripeRows:false,
39361         trackMouseOver: false,
39362         clicksToEdit:1
39363     }, config));
39364     this.getGridEl().addClass('x-props-grid');
39365     this.lastEditRow = null;
39366     this.on('columnresize', this.onColumnResize, this);
39367     this.addEvents({
39368          /**
39369              * @event beforepropertychange
39370              * Fires before a property changes (return false to stop?)
39371              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
39372              * @param {String} id Record Id
39373              * @param {String} newval New Value
39374          * @param {String} oldval Old Value
39375              */
39376         "beforepropertychange": true,
39377         /**
39378              * @event propertychange
39379              * Fires after a property changes
39380              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
39381              * @param {String} id Record Id
39382              * @param {String} newval New Value
39383          * @param {String} oldval Old Value
39384              */
39385         "propertychange": true
39386     });
39387     this.customEditors = this.customEditors || {};
39388 };
39389 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
39390     
39391      /**
39392      * @cfg {Object} customEditors map of colnames=> custom editors.
39393      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
39394      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
39395      * false disables editing of the field.
39396          */
39397     
39398       /**
39399      * @cfg {Object} propertyNames map of property Names to their displayed value
39400          */
39401     
39402     render : function(){
39403         Roo.grid.PropertyGrid.superclass.render.call(this);
39404         this.autoSize.defer(100, this);
39405     },
39406
39407     autoSize : function(){
39408         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
39409         if(this.view){
39410             this.view.fitColumns();
39411         }
39412     },
39413
39414     onColumnResize : function(){
39415         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
39416         this.autoSize();
39417     },
39418     /**
39419      * Sets the data for the Grid
39420      * accepts a Key => Value object of all the elements avaiable.
39421      * @param {Object} data  to appear in grid.
39422      */
39423     setSource : function(source){
39424         this.store.setSource(source);
39425         //this.autoSize();
39426     },
39427     /**
39428      * Gets all the data from the grid.
39429      * @return {Object} data  data stored in grid
39430      */
39431     getSource : function(){
39432         return this.store.getSource();
39433     }
39434 });/*
39435  * Based on:
39436  * Ext JS Library 1.1.1
39437  * Copyright(c) 2006-2007, Ext JS, LLC.
39438  *
39439  * Originally Released Under LGPL - original licence link has changed is not relivant.
39440  *
39441  * Fork - LGPL
39442  * <script type="text/javascript">
39443  */
39444  
39445 /**
39446  * @class Roo.LoadMask
39447  * A simple utility class for generically masking elements while loading data.  If the element being masked has
39448  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
39449  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
39450  * element's UpdateManager load indicator and will be destroyed after the initial load.
39451  * @constructor
39452  * Create a new LoadMask
39453  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
39454  * @param {Object} config The config object
39455  */
39456 Roo.LoadMask = function(el, config){
39457     this.el = Roo.get(el);
39458     Roo.apply(this, config);
39459     if(this.store){
39460         this.store.on('beforeload', this.onBeforeLoad, this);
39461         this.store.on('load', this.onLoad, this);
39462         this.store.on('loadexception', this.onLoadException, this);
39463         this.removeMask = false;
39464     }else{
39465         var um = this.el.getUpdateManager();
39466         um.showLoadIndicator = false; // disable the default indicator
39467         um.on('beforeupdate', this.onBeforeLoad, this);
39468         um.on('update', this.onLoad, this);
39469         um.on('failure', this.onLoad, this);
39470         this.removeMask = true;
39471     }
39472 };
39473
39474 Roo.LoadMask.prototype = {
39475     /**
39476      * @cfg {Boolean} removeMask
39477      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
39478      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
39479      */
39480     /**
39481      * @cfg {String} msg
39482      * The text to display in a centered loading message box (defaults to 'Loading...')
39483      */
39484     msg : 'Loading...',
39485     /**
39486      * @cfg {String} msgCls
39487      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
39488      */
39489     msgCls : 'x-mask-loading',
39490
39491     /**
39492      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
39493      * @type Boolean
39494      */
39495     disabled: false,
39496
39497     /**
39498      * Disables the mask to prevent it from being displayed
39499      */
39500     disable : function(){
39501        this.disabled = true;
39502     },
39503
39504     /**
39505      * Enables the mask so that it can be displayed
39506      */
39507     enable : function(){
39508         this.disabled = false;
39509     },
39510     
39511     onLoadException : function()
39512     {
39513         Roo.log(arguments);
39514         
39515         if (typeof(arguments[3]) != 'undefined') {
39516             Roo.MessageBox.alert("Error loading",arguments[3]);
39517         } 
39518         /*
39519         try {
39520             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
39521                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
39522             }   
39523         } catch(e) {
39524             
39525         }
39526         */
39527     
39528         
39529         
39530         this.el.unmask(this.removeMask);
39531     },
39532     // private
39533     onLoad : function()
39534     {
39535         this.el.unmask(this.removeMask);
39536     },
39537
39538     // private
39539     onBeforeLoad : function(){
39540         if(!this.disabled){
39541             this.el.mask(this.msg, this.msgCls);
39542         }
39543     },
39544
39545     // private
39546     destroy : function(){
39547         if(this.store){
39548             this.store.un('beforeload', this.onBeforeLoad, this);
39549             this.store.un('load', this.onLoad, this);
39550             this.store.un('loadexception', this.onLoadException, this);
39551         }else{
39552             var um = this.el.getUpdateManager();
39553             um.un('beforeupdate', this.onBeforeLoad, this);
39554             um.un('update', this.onLoad, this);
39555             um.un('failure', this.onLoad, this);
39556         }
39557     }
39558 };/*
39559  * Based on:
39560  * Ext JS Library 1.1.1
39561  * Copyright(c) 2006-2007, Ext JS, LLC.
39562  *
39563  * Originally Released Under LGPL - original licence link has changed is not relivant.
39564  *
39565  * Fork - LGPL
39566  * <script type="text/javascript">
39567  */
39568
39569
39570 /**
39571  * @class Roo.XTemplate
39572  * @extends Roo.Template
39573  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
39574 <pre><code>
39575 var t = new Roo.XTemplate(
39576         '&lt;select name="{name}"&gt;',
39577                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
39578         '&lt;/select&gt;'
39579 );
39580  
39581 // then append, applying the master template values
39582  </code></pre>
39583  *
39584  * Supported features:
39585  *
39586  *  Tags:
39587
39588 <pre><code>
39589       {a_variable} - output encoded.
39590       {a_variable.format:("Y-m-d")} - call a method on the variable
39591       {a_variable:raw} - unencoded output
39592       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
39593       {a_variable:this.method_on_template(...)} - call a method on the template object.
39594  
39595 </code></pre>
39596  *  The tpl tag:
39597 <pre><code>
39598         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
39599         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
39600         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
39601         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
39602   
39603         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
39604         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
39605 </code></pre>
39606  *      
39607  */
39608 Roo.XTemplate = function()
39609 {
39610     Roo.XTemplate.superclass.constructor.apply(this, arguments);
39611     if (this.html) {
39612         this.compile();
39613     }
39614 };
39615
39616
39617 Roo.extend(Roo.XTemplate, Roo.Template, {
39618
39619     /**
39620      * The various sub templates
39621      */
39622     tpls : false,
39623     /**
39624      *
39625      * basic tag replacing syntax
39626      * WORD:WORD()
39627      *
39628      * // you can fake an object call by doing this
39629      *  x.t:(test,tesT) 
39630      * 
39631      */
39632     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
39633
39634     /**
39635      * compile the template
39636      *
39637      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
39638      *
39639      */
39640     compile: function()
39641     {
39642         var s = this.html;
39643      
39644         s = ['<tpl>', s, '</tpl>'].join('');
39645     
39646         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
39647             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
39648             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
39649             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
39650             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
39651             m,
39652             id     = 0,
39653             tpls   = [];
39654     
39655         while(true == !!(m = s.match(re))){
39656             var forMatch   = m[0].match(nameRe),
39657                 ifMatch   = m[0].match(ifRe),
39658                 execMatch   = m[0].match(execRe),
39659                 namedMatch   = m[0].match(namedRe),
39660                 
39661                 exp  = null, 
39662                 fn   = null,
39663                 exec = null,
39664                 name = forMatch && forMatch[1] ? forMatch[1] : '';
39665                 
39666             if (ifMatch) {
39667                 // if - puts fn into test..
39668                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
39669                 if(exp){
39670                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
39671                 }
39672             }
39673             
39674             if (execMatch) {
39675                 // exec - calls a function... returns empty if true is  returned.
39676                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
39677                 if(exp){
39678                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
39679                 }
39680             }
39681             
39682             
39683             if (name) {
39684                 // for = 
39685                 switch(name){
39686                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
39687                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
39688                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
39689                 }
39690             }
39691             var uid = namedMatch ? namedMatch[1] : id;
39692             
39693             
39694             tpls.push({
39695                 id:     namedMatch ? namedMatch[1] : id,
39696                 target: name,
39697                 exec:   exec,
39698                 test:   fn,
39699                 body:   m[1] || ''
39700             });
39701             if (namedMatch) {
39702                 s = s.replace(m[0], '');
39703             } else { 
39704                 s = s.replace(m[0], '{xtpl'+ id + '}');
39705             }
39706             ++id;
39707         }
39708         this.tpls = [];
39709         for(var i = tpls.length-1; i >= 0; --i){
39710             this.compileTpl(tpls[i]);
39711             this.tpls[tpls[i].id] = tpls[i];
39712         }
39713         this.master = tpls[tpls.length-1];
39714         return this;
39715     },
39716     /**
39717      * same as applyTemplate, except it's done to one of the subTemplates
39718      * when using named templates, you can do:
39719      *
39720      * var str = pl.applySubTemplate('your-name', values);
39721      *
39722      * 
39723      * @param {Number} id of the template
39724      * @param {Object} values to apply to template
39725      * @param {Object} parent (normaly the instance of this object)
39726      */
39727     applySubTemplate : function(id, values, parent)
39728     {
39729         
39730         
39731         var t = this.tpls[id];
39732         
39733         
39734         try { 
39735             if(t.test && !t.test.call(this, values, parent)){
39736                 return '';
39737             }
39738         } catch(e) {
39739             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
39740             Roo.log(e.toString());
39741             Roo.log(t.test);
39742             return ''
39743         }
39744         try { 
39745             
39746             if(t.exec && t.exec.call(this, values, parent)){
39747                 return '';
39748             }
39749         } catch(e) {
39750             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
39751             Roo.log(e.toString());
39752             Roo.log(t.exec);
39753             return ''
39754         }
39755         try {
39756             var vs = t.target ? t.target.call(this, values, parent) : values;
39757             parent = t.target ? values : parent;
39758             if(t.target && vs instanceof Array){
39759                 var buf = [];
39760                 for(var i = 0, len = vs.length; i < len; i++){
39761                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
39762                 }
39763                 return buf.join('');
39764             }
39765             return t.compiled.call(this, vs, parent);
39766         } catch (e) {
39767             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
39768             Roo.log(e.toString());
39769             Roo.log(t.compiled);
39770             return '';
39771         }
39772     },
39773
39774     compileTpl : function(tpl)
39775     {
39776         var fm = Roo.util.Format;
39777         var useF = this.disableFormats !== true;
39778         var sep = Roo.isGecko ? "+" : ",";
39779         var undef = function(str) {
39780             Roo.log("Property not found :"  + str);
39781             return '';
39782         };
39783         
39784         var fn = function(m, name, format, args)
39785         {
39786             //Roo.log(arguments);
39787             args = args ? args.replace(/\\'/g,"'") : args;
39788             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
39789             if (typeof(format) == 'undefined') {
39790                 format= 'htmlEncode';
39791             }
39792             if (format == 'raw' ) {
39793                 format = false;
39794             }
39795             
39796             if(name.substr(0, 4) == 'xtpl'){
39797                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
39798             }
39799             
39800             // build an array of options to determine if value is undefined..
39801             
39802             // basically get 'xxxx.yyyy' then do
39803             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
39804             //    (function () { Roo.log("Property not found"); return ''; })() :
39805             //    ......
39806             
39807             var udef_ar = [];
39808             var lookfor = '';
39809             Roo.each(name.split('.'), function(st) {
39810                 lookfor += (lookfor.length ? '.': '') + st;
39811                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
39812             });
39813             
39814             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
39815             
39816             
39817             if(format && useF){
39818                 
39819                 args = args ? ',' + args : "";
39820                  
39821                 if(format.substr(0, 5) != "this."){
39822                     format = "fm." + format + '(';
39823                 }else{
39824                     format = 'this.call("'+ format.substr(5) + '", ';
39825                     args = ", values";
39826                 }
39827                 
39828                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
39829             }
39830              
39831             if (args.length) {
39832                 // called with xxyx.yuu:(test,test)
39833                 // change to ()
39834                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
39835             }
39836             // raw.. - :raw modifier..
39837             return "'"+ sep + udef_st  + name + ")"+sep+"'";
39838             
39839         };
39840         var body;
39841         // branched to use + in gecko and [].join() in others
39842         if(Roo.isGecko){
39843             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
39844                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
39845                     "';};};";
39846         }else{
39847             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
39848             body.push(tpl.body.replace(/(\r\n|\n)/g,
39849                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
39850             body.push("'].join('');};};");
39851             body = body.join('');
39852         }
39853         
39854         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
39855        
39856         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
39857         eval(body);
39858         
39859         return this;
39860     },
39861
39862     applyTemplate : function(values){
39863         return this.master.compiled.call(this, values, {});
39864         //var s = this.subs;
39865     },
39866
39867     apply : function(){
39868         return this.applyTemplate.apply(this, arguments);
39869     }
39870
39871  });
39872
39873 Roo.XTemplate.from = function(el){
39874     el = Roo.getDom(el);
39875     return new Roo.XTemplate(el.value || el.innerHTML);
39876 };/*
39877  * Original code for Roojs - LGPL
39878  * <script type="text/javascript">
39879  */
39880  
39881 /**
39882  * @class Roo.XComponent
39883  * A delayed Element creator...
39884  * Or a way to group chunks of interface together.
39885  * 
39886  * Mypart.xyx = new Roo.XComponent({
39887
39888     parent : 'Mypart.xyz', // empty == document.element.!!
39889     order : '001',
39890     name : 'xxxx'
39891     region : 'xxxx'
39892     disabled : function() {} 
39893      
39894     tree : function() { // return an tree of xtype declared components
39895         var MODULE = this;
39896         return 
39897         {
39898             xtype : 'NestedLayoutPanel',
39899             // technicall
39900         }
39901      ]
39902  *})
39903  *
39904  *
39905  * It can be used to build a big heiracy, with parent etc.
39906  * or you can just use this to render a single compoent to a dom element
39907  * MYPART.render(Roo.Element | String(id) | dom_element )
39908  * 
39909  * @extends Roo.util.Observable
39910  * @constructor
39911  * @param cfg {Object} configuration of component
39912  * 
39913  */
39914 Roo.XComponent = function(cfg) {
39915     Roo.apply(this, cfg);
39916     this.addEvents({ 
39917         /**
39918              * @event built
39919              * Fires when this the componnt is built
39920              * @param {Roo.XComponent} c the component
39921              */
39922         'built' : true
39923         
39924     });
39925     this.region = this.region || 'center'; // default..
39926     Roo.XComponent.register(this);
39927     this.modules = false;
39928     this.el = false; // where the layout goes..
39929     
39930     
39931 }
39932 Roo.extend(Roo.XComponent, Roo.util.Observable, {
39933     /**
39934      * @property el
39935      * The created element (with Roo.factory())
39936      * @type {Roo.Layout}
39937      */
39938     el  : false,
39939     
39940     /**
39941      * @property el
39942      * for BC  - use el in new code
39943      * @type {Roo.Layout}
39944      */
39945     panel : false,
39946     
39947     /**
39948      * @property layout
39949      * for BC  - use el in new code
39950      * @type {Roo.Layout}
39951      */
39952     layout : false,
39953     
39954      /**
39955      * @cfg {Function|boolean} disabled
39956      * If this module is disabled by some rule, return true from the funtion
39957      */
39958     disabled : false,
39959     
39960     /**
39961      * @cfg {String} parent 
39962      * Name of parent element which it get xtype added to..
39963      */
39964     parent: false,
39965     
39966     /**
39967      * @cfg {String} order
39968      * Used to set the order in which elements are created (usefull for multiple tabs)
39969      */
39970     
39971     order : false,
39972     /**
39973      * @cfg {String} name
39974      * String to display while loading.
39975      */
39976     name : false,
39977     /**
39978      * @cfg {String} region
39979      * Region to render component to (defaults to center)
39980      */
39981     region : 'center',
39982     
39983     /**
39984      * @cfg {Array} items
39985      * A single item array - the first element is the root of the tree..
39986      * It's done this way to stay compatible with the Xtype system...
39987      */
39988     items : false,
39989     
39990     /**
39991      * @property _tree
39992      * The method that retuns the tree of parts that make up this compoennt 
39993      * @type {function}
39994      */
39995     _tree  : false,
39996     
39997      /**
39998      * render
39999      * render element to dom or tree
40000      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
40001      */
40002     
40003     render : function(el)
40004     {
40005         
40006         el = el || false;
40007         var hp = this.parent ? 1 : 0;
40008         
40009         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
40010             // if parent is a '#.....' string, then let's use that..
40011             var ename = this.parent.substr(1)
40012             this.parent = false;
40013             el = Roo.get(ename);
40014             if (!el) {
40015                 Roo.log("Warning - element can not be found :#" + ename );
40016                 return;
40017             }
40018         }
40019         
40020         
40021         if (!this.parent) {
40022             
40023             el = el ? Roo.get(el) : false;      
40024             
40025             // it's a top level one..
40026             this.parent =  {
40027                 el : new Roo.BorderLayout(el || document.body, {
40028                 
40029                      center: {
40030                          titlebar: false,
40031                          autoScroll:false,
40032                          closeOnTab: true,
40033                          tabPosition: 'top',
40034                           //resizeTabs: true,
40035                          alwaysShowTabs: el && hp? false :  true,
40036                          hideTabs: el || !hp ? true :  false,
40037                          minTabWidth: 140
40038                      }
40039                  })
40040             }
40041         }
40042         
40043                 if (!this.parent.el) {
40044                         // probably an old style ctor, which has been disabled.
40045                         return;
40046                         
40047                 }
40048                 // The 'tree' method is  '_tree now' 
40049             
40050         var tree = this._tree ? this._tree() : this.tree();
40051         tree.region = tree.region || this.region;
40052         this.el = this.parent.el.addxtype(tree);
40053         this.fireEvent('built', this);
40054         
40055         this.panel = this.el;
40056         this.layout = this.panel.layout;
40057                 this.parentLayout = this.parent.layout  || false;  
40058          
40059     }
40060     
40061 });
40062
40063 Roo.apply(Roo.XComponent, {
40064     /**
40065      * @property  hideProgress
40066      * true to disable the building progress bar.. usefull on single page renders.
40067      * @type Boolean
40068      */
40069     hideProgress : false,
40070     /**
40071      * @property  buildCompleted
40072      * True when the builder has completed building the interface.
40073      * @type Boolean
40074      */
40075     buildCompleted : false,
40076      
40077     /**
40078      * @property  topModule
40079      * the upper most module - uses document.element as it's constructor.
40080      * @type Object
40081      */
40082      
40083     topModule  : false,
40084       
40085     /**
40086      * @property  modules
40087      * array of modules to be created by registration system.
40088      * @type {Array} of Roo.XComponent
40089      */
40090     
40091     modules : [],
40092     /**
40093      * @property  elmodules
40094      * array of modules to be created by which use #ID 
40095      * @type {Array} of Roo.XComponent
40096      */
40097      
40098     elmodules : [],
40099
40100     
40101     /**
40102      * Register components to be built later.
40103      *
40104      * This solves the following issues
40105      * - Building is not done on page load, but after an authentication process has occured.
40106      * - Interface elements are registered on page load
40107      * - Parent Interface elements may not be loaded before child, so this handles that..
40108      * 
40109      *
40110      * example:
40111      * 
40112      * MyApp.register({
40113           order : '000001',
40114           module : 'Pman.Tab.projectMgr',
40115           region : 'center',
40116           parent : 'Pman.layout',
40117           disabled : false,  // or use a function..
40118         })
40119      
40120      * * @param {Object} details about module
40121      */
40122     register : function(obj) {
40123                 
40124         Roo.XComponent.event.fireEvent('register', obj);
40125         switch(typeof(obj.disabled) ) {
40126                 
40127             case 'undefined':
40128                 break;
40129             
40130             case 'function':
40131                 if ( obj.disabled() ) {
40132                         return;
40133                 }
40134                 break;
40135             
40136             default:
40137                 if (obj.disabled) {
40138                         return;
40139                 }
40140                 break;
40141         }
40142                 
40143         this.modules.push(obj);
40144          
40145     },
40146     /**
40147      * convert a string to an object..
40148      * eg. 'AAA.BBB' -> finds AAA.BBB
40149
40150      */
40151     
40152     toObject : function(str)
40153     {
40154         if (!str || typeof(str) == 'object') {
40155             return str;
40156         }
40157         if (str.substring(0,1) == '#') {
40158             return str;
40159         }
40160
40161         var ar = str.split('.');
40162         var rt, o;
40163         rt = ar.shift();
40164             /** eval:var:o */
40165         try {
40166             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
40167         } catch (e) {
40168             throw "Module not found : " + str;
40169         }
40170         
40171         if (o === false) {
40172             throw "Module not found : " + str;
40173         }
40174         Roo.each(ar, function(e) {
40175             if (typeof(o[e]) == 'undefined') {
40176                 throw "Module not found : " + str;
40177             }
40178             o = o[e];
40179         });
40180         
40181         return o;
40182         
40183     },
40184     
40185     
40186     /**
40187      * move modules into their correct place in the tree..
40188      * 
40189      */
40190     preBuild : function ()
40191     {
40192         var _t = this;
40193         Roo.each(this.modules , function (obj)
40194         {
40195             Roo.XComponent.event.fireEvent('beforebuild', obj);
40196             
40197             var opar = obj.parent;
40198             try { 
40199                 obj.parent = this.toObject(opar);
40200             } catch(e) {
40201                 Roo.log("parent:toObject failed: " + e.toString());
40202                 return;
40203             }
40204             
40205             if (!obj.parent) {
40206                 Roo.debug && Roo.log("GOT top level module");
40207                 Roo.debug && Roo.log(obj);
40208                 obj.modules = new Roo.util.MixedCollection(false, 
40209                     function(o) { return o.order + '' }
40210                 );
40211                 this.topModule = obj;
40212                 return;
40213             }
40214                         // parent is a string (usually a dom element name..)
40215             if (typeof(obj.parent) == 'string') {
40216                 this.elmodules.push(obj);
40217                 return;
40218             }
40219             if (obj.parent.constructor != Roo.XComponent) {
40220                 Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
40221             }
40222             if (!obj.parent.modules) {
40223                 obj.parent.modules = new Roo.util.MixedCollection(false, 
40224                     function(o) { return o.order + '' }
40225                 );
40226             }
40227             if (obj.parent.disabled) {
40228                 obj.disabled = true;
40229             }
40230             obj.parent.modules.add(obj);
40231         }, this);
40232     },
40233     
40234      /**
40235      * make a list of modules to build.
40236      * @return {Array} list of modules. 
40237      */ 
40238     
40239     buildOrder : function()
40240     {
40241         var _this = this;
40242         var cmp = function(a,b) {   
40243             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
40244         };
40245         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
40246             throw "No top level modules to build";
40247         }
40248         
40249         // make a flat list in order of modules to build.
40250         var mods = this.topModule ? [ this.topModule ] : [];
40251                 
40252         
40253         // elmodules (is a list of DOM based modules )
40254         Roo.each(this.elmodules, function(e) {
40255             mods.push(e);
40256             if (!this.topModule &&
40257                 typeof(e.parent) == 'string' &&
40258                 e.parent.substring(0,1) == '#' &&
40259                 Roo.get(e.parent.substr(1))
40260                ) {
40261                 
40262                 _this.topModule = e;
40263             }
40264             
40265         });
40266
40267         
40268         // add modules to their parents..
40269         var addMod = function(m) {
40270             Roo.debug && Roo.log("build Order: add: " + m.name);
40271                 
40272             mods.push(m);
40273             if (m.modules && !m.disabled) {
40274                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
40275                 m.modules.keySort('ASC',  cmp );
40276                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
40277     
40278                 m.modules.each(addMod);
40279             } else {
40280                 Roo.debug && Roo.log("build Order: no child modules");
40281             }
40282             // not sure if this is used any more..
40283             if (m.finalize) {
40284                 m.finalize.name = m.name + " (clean up) ";
40285                 mods.push(m.finalize);
40286             }
40287             
40288         }
40289         if (this.topModule && this.topModule.modules) { 
40290             this.topModule.modules.keySort('ASC',  cmp );
40291             this.topModule.modules.each(addMod);
40292         } 
40293         return mods;
40294     },
40295     
40296      /**
40297      * Build the registered modules.
40298      * @param {Object} parent element.
40299      * @param {Function} optional method to call after module has been added.
40300      * 
40301      */ 
40302    
40303     build : function() 
40304     {
40305         
40306         this.preBuild();
40307         var mods = this.buildOrder();
40308       
40309         //this.allmods = mods;
40310         //Roo.debug && Roo.log(mods);
40311         //return;
40312         if (!mods.length) { // should not happen
40313             throw "NO modules!!!";
40314         }
40315         
40316         
40317         var msg = "Building Interface...";
40318         // flash it up as modal - so we store the mask!?
40319         if (!this.hideProgress) {
40320             Roo.MessageBox.show({ title: 'loading' });
40321             Roo.MessageBox.show({
40322                title: "Please wait...",
40323                msg: msg,
40324                width:450,
40325                progress:true,
40326                closable:false,
40327                modal: false
40328               
40329             });
40330         }
40331         var total = mods.length;
40332         
40333         var _this = this;
40334         var progressRun = function() {
40335             if (!mods.length) {
40336                 Roo.debug && Roo.log('hide?');
40337                 if (!this.hideProgress) {
40338                     Roo.MessageBox.hide();
40339                 }
40340                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
40341                 
40342                 // THE END...
40343                 return false;   
40344             }
40345             
40346             var m = mods.shift();
40347             
40348             
40349             Roo.debug && Roo.log(m);
40350             // not sure if this is supported any more.. - modules that are are just function
40351             if (typeof(m) == 'function') { 
40352                 m.call(this);
40353                 return progressRun.defer(10, _this);
40354             } 
40355             
40356             
40357             msg = "Building Interface " + (total  - mods.length) + 
40358                     " of " + total + 
40359                     (m.name ? (' - ' + m.name) : '');
40360                         Roo.debug && Roo.log(msg);
40361             if (!this.hideProgress) { 
40362                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
40363             }
40364             
40365          
40366             // is the module disabled?
40367             var disabled = (typeof(m.disabled) == 'function') ?
40368                 m.disabled.call(m.module.disabled) : m.disabled;    
40369             
40370             
40371             if (disabled) {
40372                 return progressRun(); // we do not update the display!
40373             }
40374             
40375             // now build 
40376             
40377                         
40378                         
40379             m.render();
40380             // it's 10 on top level, and 1 on others??? why...
40381             return progressRun.defer(10, _this);
40382              
40383         }
40384         progressRun.defer(1, _this);
40385      
40386         
40387         
40388     },
40389         
40390         
40391         /**
40392          * Event Object.
40393          *
40394          *
40395          */
40396         event: false, 
40397     /**
40398          * wrapper for event.on - aliased later..  
40399          * Typically use to register a event handler for register:
40400          *
40401          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
40402          *
40403          */
40404     on : false
40405    
40406     
40407     
40408 });
40409
40410 Roo.XComponent.event = new Roo.util.Observable({
40411                 events : { 
40412                         /**
40413                          * @event register
40414                          * Fires when an Component is registered,
40415                          * set the disable property on the Component to stop registration.
40416                          * @param {Roo.XComponent} c the component being registerd.
40417                          * 
40418                          */
40419                         'register' : true,
40420             /**
40421                          * @event beforebuild
40422                          * Fires before each Component is built
40423                          * can be used to apply permissions.
40424                          * @param {Roo.XComponent} c the component being registerd.
40425                          * 
40426                          */
40427                         'beforebuild' : true,
40428                         /**
40429                          * @event buildcomplete
40430                          * Fires on the top level element when all elements have been built
40431                          * @param {Roo.XComponent} the top level component.
40432                          */
40433                         'buildcomplete' : true
40434                         
40435                 }
40436 });
40437
40438 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
40439