roojs-core.js
[roojs1] / roojs-ui-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12
13
14 /*
15  * These classes are derivatives of the similarly named classes in the YUI Library.
16  * The original license:
17  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
18  * Code licensed under the BSD License:
19  * http://developer.yahoo.net/yui/license.txt
20  */
21
22 (function() {
23
24 var Event=Roo.EventManager;
25 var Dom=Roo.lib.Dom;
26
27 /**
28  * @class Roo.dd.DragDrop
29  * @extends Roo.util.Observable
30  * Defines the interface and base operation of items that that can be
31  * dragged or can be drop targets.  It was designed to be extended, overriding
32  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
33  * Up to three html elements can be associated with a DragDrop instance:
34  * <ul>
35  * <li>linked element: the element that is passed into the constructor.
36  * This is the element which defines the boundaries for interaction with
37  * other DragDrop objects.</li>
38  * <li>handle element(s): The drag operation only occurs if the element that
39  * was clicked matches a handle element.  By default this is the linked
40  * element, but there are times that you will want only a portion of the
41  * linked element to initiate the drag operation, and the setHandleElId()
42  * method provides a way to define this.</li>
43  * <li>drag element: this represents the element that would be moved along
44  * with the cursor during a drag operation.  By default, this is the linked
45  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
46  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
47  * </li>
48  * </ul>
49  * This class should not be instantiated until the onload event to ensure that
50  * the associated elements are available.
51  * The following would define a DragDrop obj that would interact with any
52  * other DragDrop obj in the "group1" group:
53  * <pre>
54  *  dd = new Roo.dd.DragDrop("div1", "group1");
55  * </pre>
56  * Since none of the event handlers have been implemented, nothing would
57  * actually happen if you were to run the code above.  Normally you would
58  * override this class or one of the default implementations, but you can
59  * also override the methods you want on an instance of the class...
60  * <pre>
61  *  dd.onDragDrop = function(e, id) {
62  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
63  *  }
64  * </pre>
65  * @constructor
66  * @param {String} id of the element that is linked to this instance
67  * @param {String} sGroup the group of related DragDrop objects
68  * @param {object} config an object containing configurable attributes
69  *                Valid properties for DragDrop:
70  *                    padding, isTarget, maintainOffset, primaryButtonOnly
71  */
72 Roo.dd.DragDrop = function(id, sGroup, config) {
73     if (id) {
74         this.init(id, sGroup, config);
75     }
76     
77 };
78
79 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
80
81     /**
82      * The id of the element associated with this object.  This is what we
83      * refer to as the "linked element" because the size and position of
84      * this element is used to determine when the drag and drop objects have
85      * interacted.
86      * @property id
87      * @type String
88      */
89     id: null,
90
91     /**
92      * Configuration attributes passed into the constructor
93      * @property config
94      * @type object
95      */
96     config: null,
97
98     /**
99      * The id of the element that will be dragged.  By default this is same
100      * as the linked element , but could be changed to another element. Ex:
101      * Roo.dd.DDProxy
102      * @property dragElId
103      * @type String
104      * @private
105      */
106     dragElId: null,
107
108     /**
109      * the id of the element that initiates the drag operation.  By default
110      * this is the linked element, but could be changed to be a child of this
111      * element.  This lets us do things like only starting the drag when the
112      * header element within the linked html element is clicked.
113      * @property handleElId
114      * @type String
115      * @private
116      */
117     handleElId: null,
118
119     /**
120      * An associative array of HTML tags that will be ignored if clicked.
121      * @property invalidHandleTypes
122      * @type {string: string}
123      */
124     invalidHandleTypes: null,
125
126     /**
127      * An associative array of ids for elements that will be ignored if clicked
128      * @property invalidHandleIds
129      * @type {string: string}
130      */
131     invalidHandleIds: null,
132
133     /**
134      * An indexted array of css class names for elements that will be ignored
135      * if clicked.
136      * @property invalidHandleClasses
137      * @type string[]
138      */
139     invalidHandleClasses: null,
140
141     /**
142      * The linked element's absolute X position at the time the drag was
143      * started
144      * @property startPageX
145      * @type int
146      * @private
147      */
148     startPageX: 0,
149
150     /**
151      * The linked element's absolute X position at the time the drag was
152      * started
153      * @property startPageY
154      * @type int
155      * @private
156      */
157     startPageY: 0,
158
159     /**
160      * The group defines a logical collection of DragDrop objects that are
161      * related.  Instances only get events when interacting with other
162      * DragDrop object in the same group.  This lets us define multiple
163      * groups using a single DragDrop subclass if we want.
164      * @property groups
165      * @type {string: string}
166      */
167     groups: null,
168
169     /**
170      * Individual drag/drop instances can be locked.  This will prevent
171      * onmousedown start drag.
172      * @property locked
173      * @type boolean
174      * @private
175      */
176     locked: false,
177
178     /**
179      * Lock this instance
180      * @method lock
181      */
182     lock: function() { this.locked = true; },
183
184     /**
185      * Unlock this instace
186      * @method unlock
187      */
188     unlock: function() { this.locked = false; },
189
190     /**
191      * By default, all insances can be a drop target.  This can be disabled by
192      * setting isTarget to false.
193      * @method isTarget
194      * @type boolean
195      */
196     isTarget: true,
197
198     /**
199      * The padding configured for this drag and drop object for calculating
200      * the drop zone intersection with this object.
201      * @method padding
202      * @type int[]
203      */
204     padding: null,
205
206     /**
207      * Cached reference to the linked element
208      * @property _domRef
209      * @private
210      */
211     _domRef: null,
212
213     /**
214      * Internal typeof flag
215      * @property __ygDragDrop
216      * @private
217      */
218     __ygDragDrop: true,
219
220     /**
221      * Set to true when horizontal contraints are applied
222      * @property constrainX
223      * @type boolean
224      * @private
225      */
226     constrainX: false,
227
228     /**
229      * Set to true when vertical contraints are applied
230      * @property constrainY
231      * @type boolean
232      * @private
233      */
234     constrainY: false,
235
236     /**
237      * The left constraint
238      * @property minX
239      * @type int
240      * @private
241      */
242     minX: 0,
243
244     /**
245      * The right constraint
246      * @property maxX
247      * @type int
248      * @private
249      */
250     maxX: 0,
251
252     /**
253      * The up constraint
254      * @property minY
255      * @type int
256      * @type int
257      * @private
258      */
259     minY: 0,
260
261     /**
262      * The down constraint
263      * @property maxY
264      * @type int
265      * @private
266      */
267     maxY: 0,
268
269     /**
270      * Maintain offsets when we resetconstraints.  Set to true when you want
271      * the position of the element relative to its parent to stay the same
272      * when the page changes
273      *
274      * @property maintainOffset
275      * @type boolean
276      */
277     maintainOffset: false,
278
279     /**
280      * Array of pixel locations the element will snap to if we specified a
281      * horizontal graduation/interval.  This array is generated automatically
282      * when you define a tick interval.
283      * @property xTicks
284      * @type int[]
285      */
286     xTicks: null,
287
288     /**
289      * Array of pixel locations the element will snap to if we specified a
290      * vertical graduation/interval.  This array is generated automatically
291      * when you define a tick interval.
292      * @property yTicks
293      * @type int[]
294      */
295     yTicks: null,
296
297     /**
298      * By default the drag and drop instance will only respond to the primary
299      * button click (left button for a right-handed mouse).  Set to true to
300      * allow drag and drop to start with any mouse click that is propogated
301      * by the browser
302      * @property primaryButtonOnly
303      * @type boolean
304      */
305     primaryButtonOnly: true,
306
307     /**
308      * The availabe property is false until the linked dom element is accessible.
309      * @property available
310      * @type boolean
311      */
312     available: false,
313
314     /**
315      * By default, drags can only be initiated if the mousedown occurs in the
316      * region the linked element is.  This is done in part to work around a
317      * bug in some browsers that mis-report the mousedown if the previous
318      * mouseup happened outside of the window.  This property is set to true
319      * if outer handles are defined.
320      *
321      * @property hasOuterHandles
322      * @type boolean
323      * @default false
324      */
325     hasOuterHandles: false,
326
327     /**
328      * Code that executes immediately before the startDrag event
329      * @method b4StartDrag
330      * @private
331      */
332     b4StartDrag: function(x, y) { },
333
334     /**
335      * Abstract method called after a drag/drop object is clicked
336      * and the drag or mousedown time thresholds have beeen met.
337      * @method startDrag
338      * @param {int} X click location
339      * @param {int} Y click location
340      */
341     startDrag: function(x, y) { /* override this */ },
342
343     /**
344      * Code that executes immediately before the onDrag event
345      * @method b4Drag
346      * @private
347      */
348     b4Drag: function(e) { },
349
350     /**
351      * Abstract method called during the onMouseMove event while dragging an
352      * object.
353      * @method onDrag
354      * @param {Event} e the mousemove event
355      */
356     onDrag: function(e) { /* override this */ },
357
358     /**
359      * Abstract method called when this element fist begins hovering over
360      * another DragDrop obj
361      * @method onDragEnter
362      * @param {Event} e the mousemove event
363      * @param {String|DragDrop[]} id In POINT mode, the element
364      * id this is hovering over.  In INTERSECT mode, an array of one or more
365      * dragdrop items being hovered over.
366      */
367     onDragEnter: function(e, id) { /* override this */ },
368
369     /**
370      * Code that executes immediately before the onDragOver event
371      * @method b4DragOver
372      * @private
373      */
374     b4DragOver: function(e) { },
375
376     /**
377      * Abstract method called when this element is hovering over another
378      * DragDrop obj
379      * @method onDragOver
380      * @param {Event} e the mousemove event
381      * @param {String|DragDrop[]} id In POINT mode, the element
382      * id this is hovering over.  In INTERSECT mode, an array of dd items
383      * being hovered over.
384      */
385     onDragOver: function(e, id) { /* override this */ },
386
387     /**
388      * Code that executes immediately before the onDragOut event
389      * @method b4DragOut
390      * @private
391      */
392     b4DragOut: function(e) { },
393
394     /**
395      * Abstract method called when we are no longer hovering over an element
396      * @method onDragOut
397      * @param {Event} e the mousemove event
398      * @param {String|DragDrop[]} id In POINT mode, the element
399      * id this was hovering over.  In INTERSECT mode, an array of dd items
400      * that the mouse is no longer over.
401      */
402     onDragOut: function(e, id) { /* override this */ },
403
404     /**
405      * Code that executes immediately before the onDragDrop event
406      * @method b4DragDrop
407      * @private
408      */
409     b4DragDrop: function(e) { },
410
411     /**
412      * Abstract method called when this item is dropped on another DragDrop
413      * obj
414      * @method onDragDrop
415      * @param {Event} e the mouseup event
416      * @param {String|DragDrop[]} id In POINT mode, the element
417      * id this was dropped on.  In INTERSECT mode, an array of dd items this
418      * was dropped on.
419      */
420     onDragDrop: function(e, id) { /* override this */ },
421
422     /**
423      * Abstract method called when this item is dropped on an area with no
424      * drop target
425      * @method onInvalidDrop
426      * @param {Event} e the mouseup event
427      */
428     onInvalidDrop: function(e) { /* override this */ },
429
430     /**
431      * Code that executes immediately before the endDrag event
432      * @method b4EndDrag
433      * @private
434      */
435     b4EndDrag: function(e) { },
436
437     /**
438      * Fired when we are done dragging the object
439      * @method endDrag
440      * @param {Event} e the mouseup event
441      */
442     endDrag: function(e) { /* override this */ },
443
444     /**
445      * Code executed immediately before the onMouseDown event
446      * @method b4MouseDown
447      * @param {Event} e the mousedown event
448      * @private
449      */
450     b4MouseDown: function(e) {  },
451
452     /**
453      * Event handler that fires when a drag/drop obj gets a mousedown
454      * @method onMouseDown
455      * @param {Event} e the mousedown event
456      */
457     onMouseDown: function(e) { /* override this */ },
458
459     /**
460      * Event handler that fires when a drag/drop obj gets a mouseup
461      * @method onMouseUp
462      * @param {Event} e the mouseup event
463      */
464     onMouseUp: function(e) { /* override this */ },
465
466     /**
467      * Override the onAvailable method to do what is needed after the initial
468      * position was determined.
469      * @method onAvailable
470      */
471     onAvailable: function () {
472     },
473
474     /*
475      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
476      * @type Object
477      */
478     defaultPadding : {left:0, right:0, top:0, bottom:0},
479
480     /*
481      * Initializes the drag drop object's constraints to restrict movement to a certain element.
482  *
483  * Usage:
484  <pre><code>
485  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
486                 { dragElId: "existingProxyDiv" });
487  dd.startDrag = function(){
488      this.constrainTo("parent-id");
489  };
490  </code></pre>
491  * Or you can initalize it using the {@link Roo.Element} object:
492  <pre><code>
493  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
494      startDrag : function(){
495          this.constrainTo("parent-id");
496      }
497  });
498  </code></pre>
499      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
500      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
501      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
502      * an object containing the sides to pad. For example: {right:10, bottom:10}
503      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
504      */
505     constrainTo : function(constrainTo, pad, inContent){
506         if(typeof pad == "number"){
507             pad = {left: pad, right:pad, top:pad, bottom:pad};
508         }
509         pad = pad || this.defaultPadding;
510         var b = Roo.get(this.getEl()).getBox();
511         var ce = Roo.get(constrainTo);
512         var s = ce.getScroll();
513         var c, cd = ce.dom;
514         if(cd == document.body){
515             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
516         }else{
517             xy = ce.getXY();
518             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
519         }
520
521
522         var topSpace = b.y - c.y;
523         var leftSpace = b.x - c.x;
524
525         this.resetConstraints();
526         this.setXConstraint(leftSpace - (pad.left||0), // left
527                 c.width - leftSpace - b.width - (pad.right||0) //right
528         );
529         this.setYConstraint(topSpace - (pad.top||0), //top
530                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
531         );
532     },
533
534     /**
535      * Returns a reference to the linked element
536      * @method getEl
537      * @return {HTMLElement} the html element
538      */
539     getEl: function() {
540         if (!this._domRef) {
541             this._domRef = Roo.getDom(this.id);
542         }
543
544         return this._domRef;
545     },
546
547     /**
548      * Returns a reference to the actual element to drag.  By default this is
549      * the same as the html element, but it can be assigned to another
550      * element. An example of this can be found in Roo.dd.DDProxy
551      * @method getDragEl
552      * @return {HTMLElement} the html element
553      */
554     getDragEl: function() {
555         return Roo.getDom(this.dragElId);
556     },
557
558     /**
559      * Sets up the DragDrop object.  Must be called in the constructor of any
560      * Roo.dd.DragDrop subclass
561      * @method init
562      * @param id the id of the linked element
563      * @param {String} sGroup the group of related items
564      * @param {object} config configuration attributes
565      */
566     init: function(id, sGroup, config) {
567         this.initTarget(id, sGroup, config);
568         Event.on(this.id, "mousedown", this.handleMouseDown, this);
569         // Event.on(this.id, "selectstart", Event.preventDefault);
570     },
571
572     /**
573      * Initializes Targeting functionality only... the object does not
574      * get a mousedown handler.
575      * @method initTarget
576      * @param id the id of the linked element
577      * @param {String} sGroup the group of related items
578      * @param {object} config configuration attributes
579      */
580     initTarget: function(id, sGroup, config) {
581
582         // configuration attributes
583         this.config = config || {};
584
585         // create a local reference to the drag and drop manager
586         this.DDM = Roo.dd.DDM;
587         // initialize the groups array
588         this.groups = {};
589
590         // assume that we have an element reference instead of an id if the
591         // parameter is not a string
592         if (typeof id !== "string") {
593             id = Roo.id(id);
594         }
595
596         // set the id
597         this.id = id;
598
599         // add to an interaction group
600         this.addToGroup((sGroup) ? sGroup : "default");
601
602         // We don't want to register this as the handle with the manager
603         // so we just set the id rather than calling the setter.
604         this.handleElId = id;
605
606         // the linked element is the element that gets dragged by default
607         this.setDragElId(id);
608
609         // by default, clicked anchors will not start drag operations.
610         this.invalidHandleTypes = { A: "A" };
611         this.invalidHandleIds = {};
612         this.invalidHandleClasses = [];
613
614         this.applyConfig();
615
616         this.handleOnAvailable();
617     },
618
619     /**
620      * Applies the configuration parameters that were passed into the constructor.
621      * This is supposed to happen at each level through the inheritance chain.  So
622      * a DDProxy implentation will execute apply config on DDProxy, DD, and
623      * DragDrop in order to get all of the parameters that are available in
624      * each object.
625      * @method applyConfig
626      */
627     applyConfig: function() {
628
629         // configurable properties:
630         //    padding, isTarget, maintainOffset, primaryButtonOnly
631         this.padding           = this.config.padding || [0, 0, 0, 0];
632         this.isTarget          = (this.config.isTarget !== false);
633         this.maintainOffset    = (this.config.maintainOffset);
634         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
635
636     },
637
638     /**
639      * Executed when the linked element is available
640      * @method handleOnAvailable
641      * @private
642      */
643     handleOnAvailable: function() {
644         this.available = true;
645         this.resetConstraints();
646         this.onAvailable();
647     },
648
649      /**
650      * Configures the padding for the target zone in px.  Effectively expands
651      * (or reduces) the virtual object size for targeting calculations.
652      * Supports css-style shorthand; if only one parameter is passed, all sides
653      * will have that padding, and if only two are passed, the top and bottom
654      * will have the first param, the left and right the second.
655      * @method setPadding
656      * @param {int} iTop    Top pad
657      * @param {int} iRight  Right pad
658      * @param {int} iBot    Bot pad
659      * @param {int} iLeft   Left pad
660      */
661     setPadding: function(iTop, iRight, iBot, iLeft) {
662         // this.padding = [iLeft, iRight, iTop, iBot];
663         if (!iRight && 0 !== iRight) {
664             this.padding = [iTop, iTop, iTop, iTop];
665         } else if (!iBot && 0 !== iBot) {
666             this.padding = [iTop, iRight, iTop, iRight];
667         } else {
668             this.padding = [iTop, iRight, iBot, iLeft];
669         }
670     },
671
672     /**
673      * Stores the initial placement of the linked element.
674      * @method setInitialPosition
675      * @param {int} diffX   the X offset, default 0
676      * @param {int} diffY   the Y offset, default 0
677      */
678     setInitPosition: function(diffX, diffY) {
679         var el = this.getEl();
680
681         if (!this.DDM.verifyEl(el)) {
682             return;
683         }
684
685         var dx = diffX || 0;
686         var dy = diffY || 0;
687
688         var p = Dom.getXY( el );
689
690         this.initPageX = p[0] - dx;
691         this.initPageY = p[1] - dy;
692
693         this.lastPageX = p[0];
694         this.lastPageY = p[1];
695
696
697         this.setStartPosition(p);
698     },
699
700     /**
701      * Sets the start position of the element.  This is set when the obj
702      * is initialized, the reset when a drag is started.
703      * @method setStartPosition
704      * @param pos current position (from previous lookup)
705      * @private
706      */
707     setStartPosition: function(pos) {
708         var p = pos || Dom.getXY( this.getEl() );
709         this.deltaSetXY = null;
710
711         this.startPageX = p[0];
712         this.startPageY = p[1];
713     },
714
715     /**
716      * Add this instance to a group of related drag/drop objects.  All
717      * instances belong to at least one group, and can belong to as many
718      * groups as needed.
719      * @method addToGroup
720      * @param sGroup {string} the name of the group
721      */
722     addToGroup: function(sGroup) {
723         this.groups[sGroup] = true;
724         this.DDM.regDragDrop(this, sGroup);
725     },
726
727     /**
728      * Remove's this instance from the supplied interaction group
729      * @method removeFromGroup
730      * @param {string}  sGroup  The group to drop
731      */
732     removeFromGroup: function(sGroup) {
733         if (this.groups[sGroup]) {
734             delete this.groups[sGroup];
735         }
736
737         this.DDM.removeDDFromGroup(this, sGroup);
738     },
739
740     /**
741      * Allows you to specify that an element other than the linked element
742      * will be moved with the cursor during a drag
743      * @method setDragElId
744      * @param id {string} the id of the element that will be used to initiate the drag
745      */
746     setDragElId: function(id) {
747         this.dragElId = id;
748     },
749
750     /**
751      * Allows you to specify a child of the linked element that should be
752      * used to initiate the drag operation.  An example of this would be if
753      * you have a content div with text and links.  Clicking anywhere in the
754      * content area would normally start the drag operation.  Use this method
755      * to specify that an element inside of the content div is the element
756      * that starts the drag operation.
757      * @method setHandleElId
758      * @param id {string} the id of the element that will be used to
759      * initiate the drag.
760      */
761     setHandleElId: function(id) {
762         if (typeof id !== "string") {
763             id = Roo.id(id);
764         }
765         this.handleElId = id;
766         this.DDM.regHandle(this.id, id);
767     },
768
769     /**
770      * Allows you to set an element outside of the linked element as a drag
771      * handle
772      * @method setOuterHandleElId
773      * @param id the id of the element that will be used to initiate the drag
774      */
775     setOuterHandleElId: function(id) {
776         if (typeof id !== "string") {
777             id = Roo.id(id);
778         }
779         Event.on(id, "mousedown",
780                 this.handleMouseDown, this);
781         this.setHandleElId(id);
782
783         this.hasOuterHandles = true;
784     },
785
786     /**
787      * Remove all drag and drop hooks for this element
788      * @method unreg
789      */
790     unreg: function() {
791         Event.un(this.id, "mousedown",
792                 this.handleMouseDown);
793         this._domRef = null;
794         this.DDM._remove(this);
795     },
796
797     destroy : function(){
798         this.unreg();
799     },
800
801     /**
802      * Returns true if this instance is locked, or the drag drop mgr is locked
803      * (meaning that all drag/drop is disabled on the page.)
804      * @method isLocked
805      * @return {boolean} true if this obj or all drag/drop is locked, else
806      * false
807      */
808     isLocked: function() {
809         return (this.DDM.isLocked() || this.locked);
810     },
811
812     /**
813      * Fired when this object is clicked
814      * @method handleMouseDown
815      * @param {Event} e
816      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
817      * @private
818      */
819     handleMouseDown: function(e, oDD){
820         if (this.primaryButtonOnly && e.button != 0) {
821             return;
822         }
823
824         if (this.isLocked()) {
825             return;
826         }
827
828         this.DDM.refreshCache(this.groups);
829
830         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
831         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
832         } else {
833             if (this.clickValidator(e)) {
834
835                 // set the initial element position
836                 this.setStartPosition();
837
838
839                 this.b4MouseDown(e);
840                 this.onMouseDown(e);
841
842                 this.DDM.handleMouseDown(e, this);
843
844                 this.DDM.stopEvent(e);
845             } else {
846
847
848             }
849         }
850     },
851
852     clickValidator: function(e) {
853         var target = e.getTarget();
854         return ( this.isValidHandleChild(target) &&
855                     (this.id == this.handleElId ||
856                         this.DDM.handleWasClicked(target, this.id)) );
857     },
858
859     /**
860      * Allows you to specify a tag name that should not start a drag operation
861      * when clicked.  This is designed to facilitate embedding links within a
862      * drag handle that do something other than start the drag.
863      * @method addInvalidHandleType
864      * @param {string} tagName the type of element to exclude
865      */
866     addInvalidHandleType: function(tagName) {
867         var type = tagName.toUpperCase();
868         this.invalidHandleTypes[type] = type;
869     },
870
871     /**
872      * Lets you to specify an element id for a child of a drag handle
873      * that should not initiate a drag
874      * @method addInvalidHandleId
875      * @param {string} id the element id of the element you wish to ignore
876      */
877     addInvalidHandleId: function(id) {
878         if (typeof id !== "string") {
879             id = Roo.id(id);
880         }
881         this.invalidHandleIds[id] = id;
882     },
883
884     /**
885      * Lets you specify a css class of elements that will not initiate a drag
886      * @method addInvalidHandleClass
887      * @param {string} cssClass the class of the elements you wish to ignore
888      */
889     addInvalidHandleClass: function(cssClass) {
890         this.invalidHandleClasses.push(cssClass);
891     },
892
893     /**
894      * Unsets an excluded tag name set by addInvalidHandleType
895      * @method removeInvalidHandleType
896      * @param {string} tagName the type of element to unexclude
897      */
898     removeInvalidHandleType: function(tagName) {
899         var type = tagName.toUpperCase();
900         // this.invalidHandleTypes[type] = null;
901         delete this.invalidHandleTypes[type];
902     },
903
904     /**
905      * Unsets an invalid handle id
906      * @method removeInvalidHandleId
907      * @param {string} id the id of the element to re-enable
908      */
909     removeInvalidHandleId: function(id) {
910         if (typeof id !== "string") {
911             id = Roo.id(id);
912         }
913         delete this.invalidHandleIds[id];
914     },
915
916     /**
917      * Unsets an invalid css class
918      * @method removeInvalidHandleClass
919      * @param {string} cssClass the class of the element(s) you wish to
920      * re-enable
921      */
922     removeInvalidHandleClass: function(cssClass) {
923         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
924             if (this.invalidHandleClasses[i] == cssClass) {
925                 delete this.invalidHandleClasses[i];
926             }
927         }
928     },
929
930     /**
931      * Checks the tag exclusion list to see if this click should be ignored
932      * @method isValidHandleChild
933      * @param {HTMLElement} node the HTMLElement to evaluate
934      * @return {boolean} true if this is a valid tag type, false if not
935      */
936     isValidHandleChild: function(node) {
937
938         var valid = true;
939         // var n = (node.nodeName == "#text") ? node.parentNode : node;
940         var nodeName;
941         try {
942             nodeName = node.nodeName.toUpperCase();
943         } catch(e) {
944             nodeName = node.nodeName;
945         }
946         valid = valid && !this.invalidHandleTypes[nodeName];
947         valid = valid && !this.invalidHandleIds[node.id];
948
949         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
950             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
951         }
952
953
954         return valid;
955
956     },
957
958     /**
959      * Create the array of horizontal tick marks if an interval was specified
960      * in setXConstraint().
961      * @method setXTicks
962      * @private
963      */
964     setXTicks: function(iStartX, iTickSize) {
965         this.xTicks = [];
966         this.xTickSize = iTickSize;
967
968         var tickMap = {};
969
970         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
971             if (!tickMap[i]) {
972                 this.xTicks[this.xTicks.length] = i;
973                 tickMap[i] = true;
974             }
975         }
976
977         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
978             if (!tickMap[i]) {
979                 this.xTicks[this.xTicks.length] = i;
980                 tickMap[i] = true;
981             }
982         }
983
984         this.xTicks.sort(this.DDM.numericSort) ;
985     },
986
987     /**
988      * Create the array of vertical tick marks if an interval was specified in
989      * setYConstraint().
990      * @method setYTicks
991      * @private
992      */
993     setYTicks: function(iStartY, iTickSize) {
994         this.yTicks = [];
995         this.yTickSize = iTickSize;
996
997         var tickMap = {};
998
999         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
1000             if (!tickMap[i]) {
1001                 this.yTicks[this.yTicks.length] = i;
1002                 tickMap[i] = true;
1003             }
1004         }
1005
1006         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
1007             if (!tickMap[i]) {
1008                 this.yTicks[this.yTicks.length] = i;
1009                 tickMap[i] = true;
1010             }
1011         }
1012
1013         this.yTicks.sort(this.DDM.numericSort) ;
1014     },
1015
1016     /**
1017      * By default, the element can be dragged any place on the screen.  Use
1018      * this method to limit the horizontal travel of the element.  Pass in
1019      * 0,0 for the parameters if you want to lock the drag to the y axis.
1020      * @method setXConstraint
1021      * @param {int} iLeft the number of pixels the element can move to the left
1022      * @param {int} iRight the number of pixels the element can move to the
1023      * right
1024      * @param {int} iTickSize optional parameter for specifying that the
1025      * element
1026      * should move iTickSize pixels at a time.
1027      */
1028     setXConstraint: function(iLeft, iRight, iTickSize) {
1029         this.leftConstraint = iLeft;
1030         this.rightConstraint = iRight;
1031
1032         this.minX = this.initPageX - iLeft;
1033         this.maxX = this.initPageX + iRight;
1034         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
1035
1036         this.constrainX = true;
1037     },
1038
1039     /**
1040      * Clears any constraints applied to this instance.  Also clears ticks
1041      * since they can't exist independent of a constraint at this time.
1042      * @method clearConstraints
1043      */
1044     clearConstraints: function() {
1045         this.constrainX = false;
1046         this.constrainY = false;
1047         this.clearTicks();
1048     },
1049
1050     /**
1051      * Clears any tick interval defined for this instance
1052      * @method clearTicks
1053      */
1054     clearTicks: function() {
1055         this.xTicks = null;
1056         this.yTicks = null;
1057         this.xTickSize = 0;
1058         this.yTickSize = 0;
1059     },
1060
1061     /**
1062      * By default, the element can be dragged any place on the screen.  Set
1063      * this to limit the vertical travel of the element.  Pass in 0,0 for the
1064      * parameters if you want to lock the drag to the x axis.
1065      * @method setYConstraint
1066      * @param {int} iUp the number of pixels the element can move up
1067      * @param {int} iDown the number of pixels the element can move down
1068      * @param {int} iTickSize optional parameter for specifying that the
1069      * element should move iTickSize pixels at a time.
1070      */
1071     setYConstraint: function(iUp, iDown, iTickSize) {
1072         this.topConstraint = iUp;
1073         this.bottomConstraint = iDown;
1074
1075         this.minY = this.initPageY - iUp;
1076         this.maxY = this.initPageY + iDown;
1077         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
1078
1079         this.constrainY = true;
1080
1081     },
1082
1083     /**
1084      * resetConstraints must be called if you manually reposition a dd element.
1085      * @method resetConstraints
1086      * @param {boolean} maintainOffset
1087      */
1088     resetConstraints: function() {
1089
1090
1091         // Maintain offsets if necessary
1092         if (this.initPageX || this.initPageX === 0) {
1093             // figure out how much this thing has moved
1094             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
1095             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
1096
1097             this.setInitPosition(dx, dy);
1098
1099         // This is the first time we have detected the element's position
1100         } else {
1101             this.setInitPosition();
1102         }
1103
1104         if (this.constrainX) {
1105             this.setXConstraint( this.leftConstraint,
1106                                  this.rightConstraint,
1107                                  this.xTickSize        );
1108         }
1109
1110         if (this.constrainY) {
1111             this.setYConstraint( this.topConstraint,
1112                                  this.bottomConstraint,
1113                                  this.yTickSize         );
1114         }
1115     },
1116
1117     /**
1118      * Normally the drag element is moved pixel by pixel, but we can specify
1119      * that it move a number of pixels at a time.  This method resolves the
1120      * location when we have it set up like this.
1121      * @method getTick
1122      * @param {int} val where we want to place the object
1123      * @param {int[]} tickArray sorted array of valid points
1124      * @return {int} the closest tick
1125      * @private
1126      */
1127     getTick: function(val, tickArray) {
1128
1129         if (!tickArray) {
1130             // If tick interval is not defined, it is effectively 1 pixel,
1131             // so we return the value passed to us.
1132             return val;
1133         } else if (tickArray[0] >= val) {
1134             // The value is lower than the first tick, so we return the first
1135             // tick.
1136             return tickArray[0];
1137         } else {
1138             for (var i=0, len=tickArray.length; i<len; ++i) {
1139                 var next = i + 1;
1140                 if (tickArray[next] && tickArray[next] >= val) {
1141                     var diff1 = val - tickArray[i];
1142                     var diff2 = tickArray[next] - val;
1143                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
1144                 }
1145             }
1146
1147             // The value is larger than the last tick, so we return the last
1148             // tick.
1149             return tickArray[tickArray.length - 1];
1150         }
1151     },
1152
1153     /**
1154      * toString method
1155      * @method toString
1156      * @return {string} string representation of the dd obj
1157      */
1158     toString: function() {
1159         return ("DragDrop " + this.id);
1160     }
1161
1162 });
1163
1164 })();
1165 /*
1166  * Based on:
1167  * Ext JS Library 1.1.1
1168  * Copyright(c) 2006-2007, Ext JS, LLC.
1169  *
1170  * Originally Released Under LGPL - original licence link has changed is not relivant.
1171  *
1172  * Fork - LGPL
1173  * <script type="text/javascript">
1174  */
1175
1176
1177 /**
1178  * The drag and drop utility provides a framework for building drag and drop
1179  * applications.  In addition to enabling drag and drop for specific elements,
1180  * the drag and drop elements are tracked by the manager class, and the
1181  * interactions between the various elements are tracked during the drag and
1182  * the implementing code is notified about these important moments.
1183  */
1184
1185 // Only load the library once.  Rewriting the manager class would orphan
1186 // existing drag and drop instances.
1187 if (!Roo.dd.DragDropMgr) {
1188
1189 /**
1190  * @class Roo.dd.DragDropMgr
1191  * DragDropMgr is a singleton that tracks the element interaction for
1192  * all DragDrop items in the window.  Generally, you will not call
1193  * this class directly, but it does have helper methods that could
1194  * be useful in your DragDrop implementations.
1195  * @singleton
1196  */
1197 Roo.dd.DragDropMgr = function() {
1198
1199     var Event = Roo.EventManager;
1200
1201     return {
1202
1203         /**
1204          * Two dimensional Array of registered DragDrop objects.  The first
1205          * dimension is the DragDrop item group, the second the DragDrop
1206          * object.
1207          * @property ids
1208          * @type {string: string}
1209          * @private
1210          * @static
1211          */
1212         ids: {},
1213
1214         /**
1215          * Array of element ids defined as drag handles.  Used to determine
1216          * if the element that generated the mousedown event is actually the
1217          * handle and not the html element itself.
1218          * @property handleIds
1219          * @type {string: string}
1220          * @private
1221          * @static
1222          */
1223         handleIds: {},
1224
1225         /**
1226          * the DragDrop object that is currently being dragged
1227          * @property dragCurrent
1228          * @type DragDrop
1229          * @private
1230          * @static
1231          **/
1232         dragCurrent: null,
1233
1234         /**
1235          * the DragDrop object(s) that are being hovered over
1236          * @property dragOvers
1237          * @type Array
1238          * @private
1239          * @static
1240          */
1241         dragOvers: {},
1242
1243         /**
1244          * the X distance between the cursor and the object being dragged
1245          * @property deltaX
1246          * @type int
1247          * @private
1248          * @static
1249          */
1250         deltaX: 0,
1251
1252         /**
1253          * the Y distance between the cursor and the object being dragged
1254          * @property deltaY
1255          * @type int
1256          * @private
1257          * @static
1258          */
1259         deltaY: 0,
1260
1261         /**
1262          * Flag to determine if we should prevent the default behavior of the
1263          * events we define. By default this is true, but this can be set to
1264          * false if you need the default behavior (not recommended)
1265          * @property preventDefault
1266          * @type boolean
1267          * @static
1268          */
1269         preventDefault: true,
1270
1271         /**
1272          * Flag to determine if we should stop the propagation of the events
1273          * we generate. This is true by default but you may want to set it to
1274          * false if the html element contains other features that require the
1275          * mouse click.
1276          * @property stopPropagation
1277          * @type boolean
1278          * @static
1279          */
1280         stopPropagation: true,
1281
1282         /**
1283          * Internal flag that is set to true when drag and drop has been
1284          * intialized
1285          * @property initialized
1286          * @private
1287          * @static
1288          */
1289         initalized: false,
1290
1291         /**
1292          * All drag and drop can be disabled.
1293          * @property locked
1294          * @private
1295          * @static
1296          */
1297         locked: false,
1298
1299         /**
1300          * Called the first time an element is registered.
1301          * @method init
1302          * @private
1303          * @static
1304          */
1305         init: function() {
1306             this.initialized = true;
1307         },
1308
1309         /**
1310          * In point mode, drag and drop interaction is defined by the
1311          * location of the cursor during the drag/drop
1312          * @property POINT
1313          * @type int
1314          * @static
1315          */
1316         POINT: 0,
1317
1318         /**
1319          * In intersect mode, drag and drop interactio nis defined by the
1320          * overlap of two or more drag and drop objects.
1321          * @property INTERSECT
1322          * @type int
1323          * @static
1324          */
1325         INTERSECT: 1,
1326
1327         /**
1328          * The current drag and drop mode.  Default: POINT
1329          * @property mode
1330          * @type int
1331          * @static
1332          */
1333         mode: 0,
1334
1335         /**
1336          * Runs method on all drag and drop objects
1337          * @method _execOnAll
1338          * @private
1339          * @static
1340          */
1341         _execOnAll: function(sMethod, args) {
1342             for (var i in this.ids) {
1343                 for (var j in this.ids[i]) {
1344                     var oDD = this.ids[i][j];
1345                     if (! this.isTypeOfDD(oDD)) {
1346                         continue;
1347                     }
1348                     oDD[sMethod].apply(oDD, args);
1349                 }
1350             }
1351         },
1352
1353         /**
1354          * Drag and drop initialization.  Sets up the global event handlers
1355          * @method _onLoad
1356          * @private
1357          * @static
1358          */
1359         _onLoad: function() {
1360
1361             this.init();
1362
1363
1364             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
1365             Event.on(document, "mousemove", this.handleMouseMove, this, true);
1366             Event.on(window,   "unload",    this._onUnload, this, true);
1367             Event.on(window,   "resize",    this._onResize, this, true);
1368             // Event.on(window,   "mouseout",    this._test);
1369
1370         },
1371
1372         /**
1373          * Reset constraints on all drag and drop objs
1374          * @method _onResize
1375          * @private
1376          * @static
1377          */
1378         _onResize: function(e) {
1379             this._execOnAll("resetConstraints", []);
1380         },
1381
1382         /**
1383          * Lock all drag and drop functionality
1384          * @method lock
1385          * @static
1386          */
1387         lock: function() { this.locked = true; },
1388
1389         /**
1390          * Unlock all drag and drop functionality
1391          * @method unlock
1392          * @static
1393          */
1394         unlock: function() { this.locked = false; },
1395
1396         /**
1397          * Is drag and drop locked?
1398          * @method isLocked
1399          * @return {boolean} True if drag and drop is locked, false otherwise.
1400          * @static
1401          */
1402         isLocked: function() { return this.locked; },
1403
1404         /**
1405          * Location cache that is set for all drag drop objects when a drag is
1406          * initiated, cleared when the drag is finished.
1407          * @property locationCache
1408          * @private
1409          * @static
1410          */
1411         locationCache: {},
1412
1413         /**
1414          * Set useCache to false if you want to force object the lookup of each
1415          * drag and drop linked element constantly during a drag.
1416          * @property useCache
1417          * @type boolean
1418          * @static
1419          */
1420         useCache: true,
1421
1422         /**
1423          * The number of pixels that the mouse needs to move after the
1424          * mousedown before the drag is initiated.  Default=3;
1425          * @property clickPixelThresh
1426          * @type int
1427          * @static
1428          */
1429         clickPixelThresh: 3,
1430
1431         /**
1432          * The number of milliseconds after the mousedown event to initiate the
1433          * drag if we don't get a mouseup event. Default=1000
1434          * @property clickTimeThresh
1435          * @type int
1436          * @static
1437          */
1438         clickTimeThresh: 350,
1439
1440         /**
1441          * Flag that indicates that either the drag pixel threshold or the
1442          * mousdown time threshold has been met
1443          * @property dragThreshMet
1444          * @type boolean
1445          * @private
1446          * @static
1447          */
1448         dragThreshMet: false,
1449
1450         /**
1451          * Timeout used for the click time threshold
1452          * @property clickTimeout
1453          * @type Object
1454          * @private
1455          * @static
1456          */
1457         clickTimeout: null,
1458
1459         /**
1460          * The X position of the mousedown event stored for later use when a
1461          * drag threshold is met.
1462          * @property startX
1463          * @type int
1464          * @private
1465          * @static
1466          */
1467         startX: 0,
1468
1469         /**
1470          * The Y position of the mousedown event stored for later use when a
1471          * drag threshold is met.
1472          * @property startY
1473          * @type int
1474          * @private
1475          * @static
1476          */
1477         startY: 0,
1478
1479         /**
1480          * Each DragDrop instance must be registered with the DragDropMgr.
1481          * This is executed in DragDrop.init()
1482          * @method regDragDrop
1483          * @param {DragDrop} oDD the DragDrop object to register
1484          * @param {String} sGroup the name of the group this element belongs to
1485          * @static
1486          */
1487         regDragDrop: function(oDD, sGroup) {
1488             if (!this.initialized) { this.init(); }
1489
1490             if (!this.ids[sGroup]) {
1491                 this.ids[sGroup] = {};
1492             }
1493             this.ids[sGroup][oDD.id] = oDD;
1494         },
1495
1496         /**
1497          * Removes the supplied dd instance from the supplied group. Executed
1498          * by DragDrop.removeFromGroup, so don't call this function directly.
1499          * @method removeDDFromGroup
1500          * @private
1501          * @static
1502          */
1503         removeDDFromGroup: function(oDD, sGroup) {
1504             if (!this.ids[sGroup]) {
1505                 this.ids[sGroup] = {};
1506             }
1507
1508             var obj = this.ids[sGroup];
1509             if (obj && obj[oDD.id]) {
1510                 delete obj[oDD.id];
1511             }
1512         },
1513
1514         /**
1515          * Unregisters a drag and drop item.  This is executed in
1516          * DragDrop.unreg, use that method instead of calling this directly.
1517          * @method _remove
1518          * @private
1519          * @static
1520          */
1521         _remove: function(oDD) {
1522             for (var g in oDD.groups) {
1523                 if (g && this.ids[g][oDD.id]) {
1524                     delete this.ids[g][oDD.id];
1525                 }
1526             }
1527             delete this.handleIds[oDD.id];
1528         },
1529
1530         /**
1531          * Each DragDrop handle element must be registered.  This is done
1532          * automatically when executing DragDrop.setHandleElId()
1533          * @method regHandle
1534          * @param {String} sDDId the DragDrop id this element is a handle for
1535          * @param {String} sHandleId the id of the element that is the drag
1536          * handle
1537          * @static
1538          */
1539         regHandle: function(sDDId, sHandleId) {
1540             if (!this.handleIds[sDDId]) {
1541                 this.handleIds[sDDId] = {};
1542             }
1543             this.handleIds[sDDId][sHandleId] = sHandleId;
1544         },
1545
1546         /**
1547          * Utility function to determine if a given element has been
1548          * registered as a drag drop item.
1549          * @method isDragDrop
1550          * @param {String} id the element id to check
1551          * @return {boolean} true if this element is a DragDrop item,
1552          * false otherwise
1553          * @static
1554          */
1555         isDragDrop: function(id) {
1556             return ( this.getDDById(id) ) ? true : false;
1557         },
1558
1559         /**
1560          * Returns the drag and drop instances that are in all groups the
1561          * passed in instance belongs to.
1562          * @method getRelated
1563          * @param {DragDrop} p_oDD the obj to get related data for
1564          * @param {boolean} bTargetsOnly if true, only return targetable objs
1565          * @return {DragDrop[]} the related instances
1566          * @static
1567          */
1568         getRelated: function(p_oDD, bTargetsOnly) {
1569             var oDDs = [];
1570             for (var i in p_oDD.groups) {
1571                 for (j in this.ids[i]) {
1572                     var dd = this.ids[i][j];
1573                     if (! this.isTypeOfDD(dd)) {
1574                         continue;
1575                     }
1576                     if (!bTargetsOnly || dd.isTarget) {
1577                         oDDs[oDDs.length] = dd;
1578                     }
1579                 }
1580             }
1581
1582             return oDDs;
1583         },
1584
1585         /**
1586          * Returns true if the specified dd target is a legal target for
1587          * the specifice drag obj
1588          * @method isLegalTarget
1589          * @param {DragDrop} the drag obj
1590          * @param {DragDrop} the target
1591          * @return {boolean} true if the target is a legal target for the
1592          * dd obj
1593          * @static
1594          */
1595         isLegalTarget: function (oDD, oTargetDD) {
1596             var targets = this.getRelated(oDD, true);
1597             for (var i=0, len=targets.length;i<len;++i) {
1598                 if (targets[i].id == oTargetDD.id) {
1599                     return true;
1600                 }
1601             }
1602
1603             return false;
1604         },
1605
1606         /**
1607          * My goal is to be able to transparently determine if an object is
1608          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
1609          * returns "object", oDD.constructor.toString() always returns
1610          * "DragDrop" and not the name of the subclass.  So for now it just
1611          * evaluates a well-known variable in DragDrop.
1612          * @method isTypeOfDD
1613          * @param {Object} the object to evaluate
1614          * @return {boolean} true if typeof oDD = DragDrop
1615          * @static
1616          */
1617         isTypeOfDD: function (oDD) {
1618             return (oDD && oDD.__ygDragDrop);
1619         },
1620
1621         /**
1622          * Utility function to determine if a given element has been
1623          * registered as a drag drop handle for the given Drag Drop object.
1624          * @method isHandle
1625          * @param {String} id the element id to check
1626          * @return {boolean} true if this element is a DragDrop handle, false
1627          * otherwise
1628          * @static
1629          */
1630         isHandle: function(sDDId, sHandleId) {
1631             return ( this.handleIds[sDDId] &&
1632                             this.handleIds[sDDId][sHandleId] );
1633         },
1634
1635         /**
1636          * Returns the DragDrop instance for a given id
1637          * @method getDDById
1638          * @param {String} id the id of the DragDrop object
1639          * @return {DragDrop} the drag drop object, null if it is not found
1640          * @static
1641          */
1642         getDDById: function(id) {
1643             for (var i in this.ids) {
1644                 if (this.ids[i][id]) {
1645                     return this.ids[i][id];
1646                 }
1647             }
1648             return null;
1649         },
1650
1651         /**
1652          * Fired after a registered DragDrop object gets the mousedown event.
1653          * Sets up the events required to track the object being dragged
1654          * @method handleMouseDown
1655          * @param {Event} e the event
1656          * @param oDD the DragDrop object being dragged
1657          * @private
1658          * @static
1659          */
1660         handleMouseDown: function(e, oDD) {
1661             if(Roo.QuickTips){
1662                 Roo.QuickTips.disable();
1663             }
1664             this.currentTarget = e.getTarget();
1665
1666             this.dragCurrent = oDD;
1667
1668             var el = oDD.getEl();
1669
1670             // track start position
1671             this.startX = e.getPageX();
1672             this.startY = e.getPageY();
1673
1674             this.deltaX = this.startX - el.offsetLeft;
1675             this.deltaY = this.startY - el.offsetTop;
1676
1677             this.dragThreshMet = false;
1678
1679             this.clickTimeout = setTimeout(
1680                     function() {
1681                         var DDM = Roo.dd.DDM;
1682                         DDM.startDrag(DDM.startX, DDM.startY);
1683                     },
1684                     this.clickTimeThresh );
1685         },
1686
1687         /**
1688          * Fired when either the drag pixel threshol or the mousedown hold
1689          * time threshold has been met.
1690          * @method startDrag
1691          * @param x {int} the X position of the original mousedown
1692          * @param y {int} the Y position of the original mousedown
1693          * @static
1694          */
1695         startDrag: function(x, y) {
1696             clearTimeout(this.clickTimeout);
1697             if (this.dragCurrent) {
1698                 this.dragCurrent.b4StartDrag(x, y);
1699                 this.dragCurrent.startDrag(x, y);
1700             }
1701             this.dragThreshMet = true;
1702         },
1703
1704         /**
1705          * Internal function to handle the mouseup event.  Will be invoked
1706          * from the context of the document.
1707          * @method handleMouseUp
1708          * @param {Event} e the event
1709          * @private
1710          * @static
1711          */
1712         handleMouseUp: function(e) {
1713
1714             if(Roo.QuickTips){
1715                 Roo.QuickTips.enable();
1716             }
1717             if (! this.dragCurrent) {
1718                 return;
1719             }
1720
1721             clearTimeout(this.clickTimeout);
1722
1723             if (this.dragThreshMet) {
1724                 this.fireEvents(e, true);
1725             } else {
1726             }
1727
1728             this.stopDrag(e);
1729
1730             this.stopEvent(e);
1731         },
1732
1733         /**
1734          * Utility to stop event propagation and event default, if these
1735          * features are turned on.
1736          * @method stopEvent
1737          * @param {Event} e the event as returned by this.getEvent()
1738          * @static
1739          */
1740         stopEvent: function(e){
1741             if(this.stopPropagation) {
1742                 e.stopPropagation();
1743             }
1744
1745             if (this.preventDefault) {
1746                 e.preventDefault();
1747             }
1748         },
1749
1750         /**
1751          * Internal function to clean up event handlers after the drag
1752          * operation is complete
1753          * @method stopDrag
1754          * @param {Event} e the event
1755          * @private
1756          * @static
1757          */
1758         stopDrag: function(e) {
1759             // Fire the drag end event for the item that was dragged
1760             if (this.dragCurrent) {
1761                 if (this.dragThreshMet) {
1762                     this.dragCurrent.b4EndDrag(e);
1763                     this.dragCurrent.endDrag(e);
1764                 }
1765
1766                 this.dragCurrent.onMouseUp(e);
1767             }
1768
1769             this.dragCurrent = null;
1770             this.dragOvers = {};
1771         },
1772
1773         /**
1774          * Internal function to handle the mousemove event.  Will be invoked
1775          * from the context of the html element.
1776          *
1777          * @TODO figure out what we can do about mouse events lost when the
1778          * user drags objects beyond the window boundary.  Currently we can
1779          * detect this in internet explorer by verifying that the mouse is
1780          * down during the mousemove event.  Firefox doesn't give us the
1781          * button state on the mousemove event.
1782          * @method handleMouseMove
1783          * @param {Event} e the event
1784          * @private
1785          * @static
1786          */
1787         handleMouseMove: function(e) {
1788             if (! this.dragCurrent) {
1789                 return true;
1790             }
1791
1792             // var button = e.which || e.button;
1793
1794             // check for IE mouseup outside of page boundary
1795             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
1796                 this.stopEvent(e);
1797                 return this.handleMouseUp(e);
1798             }
1799
1800             if (!this.dragThreshMet) {
1801                 var diffX = Math.abs(this.startX - e.getPageX());
1802                 var diffY = Math.abs(this.startY - e.getPageY());
1803                 if (diffX > this.clickPixelThresh ||
1804                             diffY > this.clickPixelThresh) {
1805                     this.startDrag(this.startX, this.startY);
1806                 }
1807             }
1808
1809             if (this.dragThreshMet) {
1810                 this.dragCurrent.b4Drag(e);
1811                 this.dragCurrent.onDrag(e);
1812                 if(!this.dragCurrent.moveOnly){
1813                     this.fireEvents(e, false);
1814                 }
1815             }
1816
1817             this.stopEvent(e);
1818
1819             return true;
1820         },
1821
1822         /**
1823          * Iterates over all of the DragDrop elements to find ones we are
1824          * hovering over or dropping on
1825          * @method fireEvents
1826          * @param {Event} e the event
1827          * @param {boolean} isDrop is this a drop op or a mouseover op?
1828          * @private
1829          * @static
1830          */
1831         fireEvents: function(e, isDrop) {
1832             var dc = this.dragCurrent;
1833
1834             // If the user did the mouse up outside of the window, we could
1835             // get here even though we have ended the drag.
1836             if (!dc || dc.isLocked()) {
1837                 return;
1838             }
1839
1840             var pt = e.getPoint();
1841
1842             // cache the previous dragOver array
1843             var oldOvers = [];
1844
1845             var outEvts   = [];
1846             var overEvts  = [];
1847             var dropEvts  = [];
1848             var enterEvts = [];
1849
1850             // Check to see if the object(s) we were hovering over is no longer
1851             // being hovered over so we can fire the onDragOut event
1852             for (var i in this.dragOvers) {
1853
1854                 var ddo = this.dragOvers[i];
1855
1856                 if (! this.isTypeOfDD(ddo)) {
1857                     continue;
1858                 }
1859
1860                 if (! this.isOverTarget(pt, ddo, this.mode)) {
1861                     outEvts.push( ddo );
1862                 }
1863
1864                 oldOvers[i] = true;
1865                 delete this.dragOvers[i];
1866             }
1867
1868             for (var sGroup in dc.groups) {
1869
1870                 if ("string" != typeof sGroup) {
1871                     continue;
1872                 }
1873
1874                 for (i in this.ids[sGroup]) {
1875                     var oDD = this.ids[sGroup][i];
1876                     if (! this.isTypeOfDD(oDD)) {
1877                         continue;
1878                     }
1879
1880                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
1881                         if (this.isOverTarget(pt, oDD, this.mode)) {
1882                             // look for drop interactions
1883                             if (isDrop) {
1884                                 dropEvts.push( oDD );
1885                             // look for drag enter and drag over interactions
1886                             } else {
1887
1888                                 // initial drag over: dragEnter fires
1889                                 if (!oldOvers[oDD.id]) {
1890                                     enterEvts.push( oDD );
1891                                 // subsequent drag overs: dragOver fires
1892                                 } else {
1893                                     overEvts.push( oDD );
1894                                 }
1895
1896                                 this.dragOvers[oDD.id] = oDD;
1897                             }
1898                         }
1899                     }
1900                 }
1901             }
1902
1903             if (this.mode) {
1904                 if (outEvts.length) {
1905                     dc.b4DragOut(e, outEvts);
1906                     dc.onDragOut(e, outEvts);
1907                 }
1908
1909                 if (enterEvts.length) {
1910                     dc.onDragEnter(e, enterEvts);
1911                 }
1912
1913                 if (overEvts.length) {
1914                     dc.b4DragOver(e, overEvts);
1915                     dc.onDragOver(e, overEvts);
1916                 }
1917
1918                 if (dropEvts.length) {
1919                     dc.b4DragDrop(e, dropEvts);
1920                     dc.onDragDrop(e, dropEvts);
1921                 }
1922
1923             } else {
1924                 // fire dragout events
1925                 var len = 0;
1926                 for (i=0, len=outEvts.length; i<len; ++i) {
1927                     dc.b4DragOut(e, outEvts[i].id);
1928                     dc.onDragOut(e, outEvts[i].id);
1929                 }
1930
1931                 // fire enter events
1932                 for (i=0,len=enterEvts.length; i<len; ++i) {
1933                     // dc.b4DragEnter(e, oDD.id);
1934                     dc.onDragEnter(e, enterEvts[i].id);
1935                 }
1936
1937                 // fire over events
1938                 for (i=0,len=overEvts.length; i<len; ++i) {
1939                     dc.b4DragOver(e, overEvts[i].id);
1940                     dc.onDragOver(e, overEvts[i].id);
1941                 }
1942
1943                 // fire drop events
1944                 for (i=0, len=dropEvts.length; i<len; ++i) {
1945                     dc.b4DragDrop(e, dropEvts[i].id);
1946                     dc.onDragDrop(e, dropEvts[i].id);
1947                 }
1948
1949             }
1950
1951             // notify about a drop that did not find a target
1952             if (isDrop && !dropEvts.length) {
1953                 dc.onInvalidDrop(e);
1954             }
1955
1956         },
1957
1958         /**
1959          * Helper function for getting the best match from the list of drag
1960          * and drop objects returned by the drag and drop events when we are
1961          * in INTERSECT mode.  It returns either the first object that the
1962          * cursor is over, or the object that has the greatest overlap with
1963          * the dragged element.
1964          * @method getBestMatch
1965          * @param  {DragDrop[]} dds The array of drag and drop objects
1966          * targeted
1967          * @return {DragDrop}       The best single match
1968          * @static
1969          */
1970         getBestMatch: function(dds) {
1971             var winner = null;
1972             // Return null if the input is not what we expect
1973             //if (!dds || !dds.length || dds.length == 0) {
1974                // winner = null;
1975             // If there is only one item, it wins
1976             //} else if (dds.length == 1) {
1977
1978             var len = dds.length;
1979
1980             if (len == 1) {
1981                 winner = dds[0];
1982             } else {
1983                 // Loop through the targeted items
1984                 for (var i=0; i<len; ++i) {
1985                     var dd = dds[i];
1986                     // If the cursor is over the object, it wins.  If the
1987                     // cursor is over multiple matches, the first one we come
1988                     // to wins.
1989                     if (dd.cursorIsOver) {
1990                         winner = dd;
1991                         break;
1992                     // Otherwise the object with the most overlap wins
1993                     } else {
1994                         if (!winner ||
1995                             winner.overlap.getArea() < dd.overlap.getArea()) {
1996                             winner = dd;
1997                         }
1998                     }
1999                 }
2000             }
2001
2002             return winner;
2003         },
2004
2005         /**
2006          * Refreshes the cache of the top-left and bottom-right points of the
2007          * drag and drop objects in the specified group(s).  This is in the
2008          * format that is stored in the drag and drop instance, so typical
2009          * usage is:
2010          * <code>
2011          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
2012          * </code>
2013          * Alternatively:
2014          * <code>
2015          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
2016          * </code>
2017          * @TODO this really should be an indexed array.  Alternatively this
2018          * method could accept both.
2019          * @method refreshCache
2020          * @param {Object} groups an associative array of groups to refresh
2021          * @static
2022          */
2023         refreshCache: function(groups) {
2024             for (var sGroup in groups) {
2025                 if ("string" != typeof sGroup) {
2026                     continue;
2027                 }
2028                 for (var i in this.ids[sGroup]) {
2029                     var oDD = this.ids[sGroup][i];
2030
2031                     if (this.isTypeOfDD(oDD)) {
2032                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
2033                         var loc = this.getLocation(oDD);
2034                         if (loc) {
2035                             this.locationCache[oDD.id] = loc;
2036                         } else {
2037                             delete this.locationCache[oDD.id];
2038                             // this will unregister the drag and drop object if
2039                             // the element is not in a usable state
2040                             // oDD.unreg();
2041                         }
2042                     }
2043                 }
2044             }
2045         },
2046
2047         /**
2048          * This checks to make sure an element exists and is in the DOM.  The
2049          * main purpose is to handle cases where innerHTML is used to remove
2050          * drag and drop objects from the DOM.  IE provides an 'unspecified
2051          * error' when trying to access the offsetParent of such an element
2052          * @method verifyEl
2053          * @param {HTMLElement} el the element to check
2054          * @return {boolean} true if the element looks usable
2055          * @static
2056          */
2057         verifyEl: function(el) {
2058             if (el) {
2059                 var parent;
2060                 if(Roo.isIE){
2061                     try{
2062                         parent = el.offsetParent;
2063                     }catch(e){}
2064                 }else{
2065                     parent = el.offsetParent;
2066                 }
2067                 if (parent) {
2068                     return true;
2069                 }
2070             }
2071
2072             return false;
2073         },
2074
2075         /**
2076          * Returns a Region object containing the drag and drop element's position
2077          * and size, including the padding configured for it
2078          * @method getLocation
2079          * @param {DragDrop} oDD the drag and drop object to get the
2080          *                       location for
2081          * @return {Roo.lib.Region} a Region object representing the total area
2082          *                             the element occupies, including any padding
2083          *                             the instance is configured for.
2084          * @static
2085          */
2086         getLocation: function(oDD) {
2087             if (! this.isTypeOfDD(oDD)) {
2088                 return null;
2089             }
2090
2091             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
2092
2093             try {
2094                 pos= Roo.lib.Dom.getXY(el);
2095             } catch (e) { }
2096
2097             if (!pos) {
2098                 return null;
2099             }
2100
2101             x1 = pos[0];
2102             x2 = x1 + el.offsetWidth;
2103             y1 = pos[1];
2104             y2 = y1 + el.offsetHeight;
2105
2106             t = y1 - oDD.padding[0];
2107             r = x2 + oDD.padding[1];
2108             b = y2 + oDD.padding[2];
2109             l = x1 - oDD.padding[3];
2110
2111             return new Roo.lib.Region( t, r, b, l );
2112         },
2113
2114         /**
2115          * Checks the cursor location to see if it over the target
2116          * @method isOverTarget
2117          * @param {Roo.lib.Point} pt The point to evaluate
2118          * @param {DragDrop} oTarget the DragDrop object we are inspecting
2119          * @return {boolean} true if the mouse is over the target
2120          * @private
2121          * @static
2122          */
2123         isOverTarget: function(pt, oTarget, intersect) {
2124             // use cache if available
2125             var loc = this.locationCache[oTarget.id];
2126             if (!loc || !this.useCache) {
2127                 loc = this.getLocation(oTarget);
2128                 this.locationCache[oTarget.id] = loc;
2129
2130             }
2131
2132             if (!loc) {
2133                 return false;
2134             }
2135
2136             oTarget.cursorIsOver = loc.contains( pt );
2137
2138             // DragDrop is using this as a sanity check for the initial mousedown
2139             // in this case we are done.  In POINT mode, if the drag obj has no
2140             // contraints, we are also done. Otherwise we need to evaluate the
2141             // location of the target as related to the actual location of the
2142             // dragged element.
2143             var dc = this.dragCurrent;
2144             if (!dc || !dc.getTargetCoord ||
2145                     (!intersect && !dc.constrainX && !dc.constrainY)) {
2146                 return oTarget.cursorIsOver;
2147             }
2148
2149             oTarget.overlap = null;
2150
2151             // Get the current location of the drag element, this is the
2152             // location of the mouse event less the delta that represents
2153             // where the original mousedown happened on the element.  We
2154             // need to consider constraints and ticks as well.
2155             var pos = dc.getTargetCoord(pt.x, pt.y);
2156
2157             var el = dc.getDragEl();
2158             var curRegion = new Roo.lib.Region( pos.y,
2159                                                    pos.x + el.offsetWidth,
2160                                                    pos.y + el.offsetHeight,
2161                                                    pos.x );
2162
2163             var overlap = curRegion.intersect(loc);
2164
2165             if (overlap) {
2166                 oTarget.overlap = overlap;
2167                 return (intersect) ? true : oTarget.cursorIsOver;
2168             } else {
2169                 return false;
2170             }
2171         },
2172
2173         /**
2174          * unload event handler
2175          * @method _onUnload
2176          * @private
2177          * @static
2178          */
2179         _onUnload: function(e, me) {
2180             Roo.dd.DragDropMgr.unregAll();
2181         },
2182
2183         /**
2184          * Cleans up the drag and drop events and objects.
2185          * @method unregAll
2186          * @private
2187          * @static
2188          */
2189         unregAll: function() {
2190
2191             if (this.dragCurrent) {
2192                 this.stopDrag();
2193                 this.dragCurrent = null;
2194             }
2195
2196             this._execOnAll("unreg", []);
2197
2198             for (i in this.elementCache) {
2199                 delete this.elementCache[i];
2200             }
2201
2202             this.elementCache = {};
2203             this.ids = {};
2204         },
2205
2206         /**
2207          * A cache of DOM elements
2208          * @property elementCache
2209          * @private
2210          * @static
2211          */
2212         elementCache: {},
2213
2214         /**
2215          * Get the wrapper for the DOM element specified
2216          * @method getElWrapper
2217          * @param {String} id the id of the element to get
2218          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
2219          * @private
2220          * @deprecated This wrapper isn't that useful
2221          * @static
2222          */
2223         getElWrapper: function(id) {
2224             var oWrapper = this.elementCache[id];
2225             if (!oWrapper || !oWrapper.el) {
2226                 oWrapper = this.elementCache[id] =
2227                     new this.ElementWrapper(Roo.getDom(id));
2228             }
2229             return oWrapper;
2230         },
2231
2232         /**
2233          * Returns the actual DOM element
2234          * @method getElement
2235          * @param {String} id the id of the elment to get
2236          * @return {Object} The element
2237          * @deprecated use Roo.getDom instead
2238          * @static
2239          */
2240         getElement: function(id) {
2241             return Roo.getDom(id);
2242         },
2243
2244         /**
2245          * Returns the style property for the DOM element (i.e.,
2246          * document.getElById(id).style)
2247          * @method getCss
2248          * @param {String} id the id of the elment to get
2249          * @return {Object} The style property of the element
2250          * @deprecated use Roo.getDom instead
2251          * @static
2252          */
2253         getCss: function(id) {
2254             var el = Roo.getDom(id);
2255             return (el) ? el.style : null;
2256         },
2257
2258         /**
2259          * Inner class for cached elements
2260          * @class DragDropMgr.ElementWrapper
2261          * @for DragDropMgr
2262          * @private
2263          * @deprecated
2264          */
2265         ElementWrapper: function(el) {
2266                 /**
2267                  * The element
2268                  * @property el
2269                  */
2270                 this.el = el || null;
2271                 /**
2272                  * The element id
2273                  * @property id
2274                  */
2275                 this.id = this.el && el.id;
2276                 /**
2277                  * A reference to the style property
2278                  * @property css
2279                  */
2280                 this.css = this.el && el.style;
2281             },
2282
2283         /**
2284          * Returns the X position of an html element
2285          * @method getPosX
2286          * @param el the element for which to get the position
2287          * @return {int} the X coordinate
2288          * @for DragDropMgr
2289          * @deprecated use Roo.lib.Dom.getX instead
2290          * @static
2291          */
2292         getPosX: function(el) {
2293             return Roo.lib.Dom.getX(el);
2294         },
2295
2296         /**
2297          * Returns the Y position of an html element
2298          * @method getPosY
2299          * @param el the element for which to get the position
2300          * @return {int} the Y coordinate
2301          * @deprecated use Roo.lib.Dom.getY instead
2302          * @static
2303          */
2304         getPosY: function(el) {
2305             return Roo.lib.Dom.getY(el);
2306         },
2307
2308         /**
2309          * Swap two nodes.  In IE, we use the native method, for others we
2310          * emulate the IE behavior
2311          * @method swapNode
2312          * @param n1 the first node to swap
2313          * @param n2 the other node to swap
2314          * @static
2315          */
2316         swapNode: function(n1, n2) {
2317             if (n1.swapNode) {
2318                 n1.swapNode(n2);
2319             } else {
2320                 var p = n2.parentNode;
2321                 var s = n2.nextSibling;
2322
2323                 if (s == n1) {
2324                     p.insertBefore(n1, n2);
2325                 } else if (n2 == n1.nextSibling) {
2326                     p.insertBefore(n2, n1);
2327                 } else {
2328                     n1.parentNode.replaceChild(n2, n1);
2329                     p.insertBefore(n1, s);
2330                 }
2331             }
2332         },
2333
2334         /**
2335          * Returns the current scroll position
2336          * @method getScroll
2337          * @private
2338          * @static
2339          */
2340         getScroll: function () {
2341             var t, l, dde=document.documentElement, db=document.body;
2342             if (dde && (dde.scrollTop || dde.scrollLeft)) {
2343                 t = dde.scrollTop;
2344                 l = dde.scrollLeft;
2345             } else if (db) {
2346                 t = db.scrollTop;
2347                 l = db.scrollLeft;
2348             } else {
2349
2350             }
2351             return { top: t, left: l };
2352         },
2353
2354         /**
2355          * Returns the specified element style property
2356          * @method getStyle
2357          * @param {HTMLElement} el          the element
2358          * @param {string}      styleProp   the style property
2359          * @return {string} The value of the style property
2360          * @deprecated use Roo.lib.Dom.getStyle
2361          * @static
2362          */
2363         getStyle: function(el, styleProp) {
2364             return Roo.fly(el).getStyle(styleProp);
2365         },
2366
2367         /**
2368          * Gets the scrollTop
2369          * @method getScrollTop
2370          * @return {int} the document's scrollTop
2371          * @static
2372          */
2373         getScrollTop: function () { return this.getScroll().top; },
2374
2375         /**
2376          * Gets the scrollLeft
2377          * @method getScrollLeft
2378          * @return {int} the document's scrollTop
2379          * @static
2380          */
2381         getScrollLeft: function () { return this.getScroll().left; },
2382
2383         /**
2384          * Sets the x/y position of an element to the location of the
2385          * target element.
2386          * @method moveToEl
2387          * @param {HTMLElement} moveEl      The element to move
2388          * @param {HTMLElement} targetEl    The position reference element
2389          * @static
2390          */
2391         moveToEl: function (moveEl, targetEl) {
2392             var aCoord = Roo.lib.Dom.getXY(targetEl);
2393             Roo.lib.Dom.setXY(moveEl, aCoord);
2394         },
2395
2396         /**
2397          * Numeric array sort function
2398          * @method numericSort
2399          * @static
2400          */
2401         numericSort: function(a, b) { return (a - b); },
2402
2403         /**
2404          * Internal counter
2405          * @property _timeoutCount
2406          * @private
2407          * @static
2408          */
2409         _timeoutCount: 0,
2410
2411         /**
2412          * Trying to make the load order less important.  Without this we get
2413          * an error if this file is loaded before the Event Utility.
2414          * @method _addListeners
2415          * @private
2416          * @static
2417          */
2418         _addListeners: function() {
2419             var DDM = Roo.dd.DDM;
2420             if ( Roo.lib.Event && document ) {
2421                 DDM._onLoad();
2422             } else {
2423                 if (DDM._timeoutCount > 2000) {
2424                 } else {
2425                     setTimeout(DDM._addListeners, 10);
2426                     if (document && document.body) {
2427                         DDM._timeoutCount += 1;
2428                     }
2429                 }
2430             }
2431         },
2432
2433         /**
2434          * Recursively searches the immediate parent and all child nodes for
2435          * the handle element in order to determine wheter or not it was
2436          * clicked.
2437          * @method handleWasClicked
2438          * @param node the html element to inspect
2439          * @static
2440          */
2441         handleWasClicked: function(node, id) {
2442             if (this.isHandle(id, node.id)) {
2443                 return true;
2444             } else {
2445                 // check to see if this is a text node child of the one we want
2446                 var p = node.parentNode;
2447
2448                 while (p) {
2449                     if (this.isHandle(id, p.id)) {
2450                         return true;
2451                     } else {
2452                         p = p.parentNode;
2453                     }
2454                 }
2455             }
2456
2457             return false;
2458         }
2459
2460     };
2461
2462 }();
2463
2464 // shorter alias, save a few bytes
2465 Roo.dd.DDM = Roo.dd.DragDropMgr;
2466 Roo.dd.DDM._addListeners();
2467
2468 }/*
2469  * Based on:
2470  * Ext JS Library 1.1.1
2471  * Copyright(c) 2006-2007, Ext JS, LLC.
2472  *
2473  * Originally Released Under LGPL - original licence link has changed is not relivant.
2474  *
2475  * Fork - LGPL
2476  * <script type="text/javascript">
2477  */
2478
2479 /**
2480  * @class Roo.dd.DD
2481  * A DragDrop implementation where the linked element follows the
2482  * mouse cursor during a drag.
2483  * @extends Roo.dd.DragDrop
2484  * @constructor
2485  * @param {String} id the id of the linked element
2486  * @param {String} sGroup the group of related DragDrop items
2487  * @param {object} config an object containing configurable attributes
2488  *                Valid properties for DD:
2489  *                    scroll
2490  */
2491 Roo.dd.DD = function(id, sGroup, config) {
2492     if (id) {
2493         this.init(id, sGroup, config);
2494     }
2495 };
2496
2497 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
2498
2499     /**
2500      * When set to true, the utility automatically tries to scroll the browser
2501      * window wehn a drag and drop element is dragged near the viewport boundary.
2502      * Defaults to true.
2503      * @property scroll
2504      * @type boolean
2505      */
2506     scroll: true,
2507
2508     /**
2509      * Sets the pointer offset to the distance between the linked element's top
2510      * left corner and the location the element was clicked
2511      * @method autoOffset
2512      * @param {int} iPageX the X coordinate of the click
2513      * @param {int} iPageY the Y coordinate of the click
2514      */
2515     autoOffset: function(iPageX, iPageY) {
2516         var x = iPageX - this.startPageX;
2517         var y = iPageY - this.startPageY;
2518         this.setDelta(x, y);
2519     },
2520
2521     /**
2522      * Sets the pointer offset.  You can call this directly to force the
2523      * offset to be in a particular location (e.g., pass in 0,0 to set it
2524      * to the center of the object)
2525      * @method setDelta
2526      * @param {int} iDeltaX the distance from the left
2527      * @param {int} iDeltaY the distance from the top
2528      */
2529     setDelta: function(iDeltaX, iDeltaY) {
2530         this.deltaX = iDeltaX;
2531         this.deltaY = iDeltaY;
2532     },
2533
2534     /**
2535      * Sets the drag element to the location of the mousedown or click event,
2536      * maintaining the cursor location relative to the location on the element
2537      * that was clicked.  Override this if you want to place the element in a
2538      * location other than where the cursor is.
2539      * @method setDragElPos
2540      * @param {int} iPageX the X coordinate of the mousedown or drag event
2541      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2542      */
2543     setDragElPos: function(iPageX, iPageY) {
2544         // the first time we do this, we are going to check to make sure
2545         // the element has css positioning
2546
2547         var el = this.getDragEl();
2548         this.alignElWithMouse(el, iPageX, iPageY);
2549     },
2550
2551     /**
2552      * Sets the element to the location of the mousedown or click event,
2553      * maintaining the cursor location relative to the location on the element
2554      * that was clicked.  Override this if you want to place the element in a
2555      * location other than where the cursor is.
2556      * @method alignElWithMouse
2557      * @param {HTMLElement} el the element to move
2558      * @param {int} iPageX the X coordinate of the mousedown or drag event
2559      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2560      */
2561     alignElWithMouse: function(el, iPageX, iPageY) {
2562         var oCoord = this.getTargetCoord(iPageX, iPageY);
2563         var fly = el.dom ? el : Roo.fly(el);
2564         if (!this.deltaSetXY) {
2565             var aCoord = [oCoord.x, oCoord.y];
2566             fly.setXY(aCoord);
2567             var newLeft = fly.getLeft(true);
2568             var newTop  = fly.getTop(true);
2569             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
2570         } else {
2571             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
2572         }
2573
2574         this.cachePosition(oCoord.x, oCoord.y);
2575         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
2576         return oCoord;
2577     },
2578
2579     /**
2580      * Saves the most recent position so that we can reset the constraints and
2581      * tick marks on-demand.  We need to know this so that we can calculate the
2582      * number of pixels the element is offset from its original position.
2583      * @method cachePosition
2584      * @param iPageX the current x position (optional, this just makes it so we
2585      * don't have to look it up again)
2586      * @param iPageY the current y position (optional, this just makes it so we
2587      * don't have to look it up again)
2588      */
2589     cachePosition: function(iPageX, iPageY) {
2590         if (iPageX) {
2591             this.lastPageX = iPageX;
2592             this.lastPageY = iPageY;
2593         } else {
2594             var aCoord = Roo.lib.Dom.getXY(this.getEl());
2595             this.lastPageX = aCoord[0];
2596             this.lastPageY = aCoord[1];
2597         }
2598     },
2599
2600     /**
2601      * Auto-scroll the window if the dragged object has been moved beyond the
2602      * visible window boundary.
2603      * @method autoScroll
2604      * @param {int} x the drag element's x position
2605      * @param {int} y the drag element's y position
2606      * @param {int} h the height of the drag element
2607      * @param {int} w the width of the drag element
2608      * @private
2609      */
2610     autoScroll: function(x, y, h, w) {
2611
2612         if (this.scroll) {
2613             // The client height
2614             var clientH = Roo.lib.Dom.getViewWidth();
2615
2616             // The client width
2617             var clientW = Roo.lib.Dom.getViewHeight();
2618
2619             // The amt scrolled down
2620             var st = this.DDM.getScrollTop();
2621
2622             // The amt scrolled right
2623             var sl = this.DDM.getScrollLeft();
2624
2625             // Location of the bottom of the element
2626             var bot = h + y;
2627
2628             // Location of the right of the element
2629             var right = w + x;
2630
2631             // The distance from the cursor to the bottom of the visible area,
2632             // adjusted so that we don't scroll if the cursor is beyond the
2633             // element drag constraints
2634             var toBot = (clientH + st - y - this.deltaY);
2635
2636             // The distance from the cursor to the right of the visible area
2637             var toRight = (clientW + sl - x - this.deltaX);
2638
2639
2640             // How close to the edge the cursor must be before we scroll
2641             // var thresh = (document.all) ? 100 : 40;
2642             var thresh = 40;
2643
2644             // How many pixels to scroll per autoscroll op.  This helps to reduce
2645             // clunky scrolling. IE is more sensitive about this ... it needs this
2646             // value to be higher.
2647             var scrAmt = (document.all) ? 80 : 30;
2648
2649             // Scroll down if we are near the bottom of the visible page and the
2650             // obj extends below the crease
2651             if ( bot > clientH && toBot < thresh ) {
2652                 window.scrollTo(sl, st + scrAmt);
2653             }
2654
2655             // Scroll up if the window is scrolled down and the top of the object
2656             // goes above the top border
2657             if ( y < st && st > 0 && y - st < thresh ) {
2658                 window.scrollTo(sl, st - scrAmt);
2659             }
2660
2661             // Scroll right if the obj is beyond the right border and the cursor is
2662             // near the border.
2663             if ( right > clientW && toRight < thresh ) {
2664                 window.scrollTo(sl + scrAmt, st);
2665             }
2666
2667             // Scroll left if the window has been scrolled to the right and the obj
2668             // extends past the left border
2669             if ( x < sl && sl > 0 && x - sl < thresh ) {
2670                 window.scrollTo(sl - scrAmt, st);
2671             }
2672         }
2673     },
2674
2675     /**
2676      * Finds the location the element should be placed if we want to move
2677      * it to where the mouse location less the click offset would place us.
2678      * @method getTargetCoord
2679      * @param {int} iPageX the X coordinate of the click
2680      * @param {int} iPageY the Y coordinate of the click
2681      * @return an object that contains the coordinates (Object.x and Object.y)
2682      * @private
2683      */
2684     getTargetCoord: function(iPageX, iPageY) {
2685
2686
2687         var x = iPageX - this.deltaX;
2688         var y = iPageY - this.deltaY;
2689
2690         if (this.constrainX) {
2691             if (x < this.minX) { x = this.minX; }
2692             if (x > this.maxX) { x = this.maxX; }
2693         }
2694
2695         if (this.constrainY) {
2696             if (y < this.minY) { y = this.minY; }
2697             if (y > this.maxY) { y = this.maxY; }
2698         }
2699
2700         x = this.getTick(x, this.xTicks);
2701         y = this.getTick(y, this.yTicks);
2702
2703
2704         return {x:x, y:y};
2705     },
2706
2707     /*
2708      * Sets up config options specific to this class. Overrides
2709      * Roo.dd.DragDrop, but all versions of this method through the
2710      * inheritance chain are called
2711      */
2712     applyConfig: function() {
2713         Roo.dd.DD.superclass.applyConfig.call(this);
2714         this.scroll = (this.config.scroll !== false);
2715     },
2716
2717     /*
2718      * Event that fires prior to the onMouseDown event.  Overrides
2719      * Roo.dd.DragDrop.
2720      */
2721     b4MouseDown: function(e) {
2722         // this.resetConstraints();
2723         this.autoOffset(e.getPageX(),
2724                             e.getPageY());
2725     },
2726
2727     /*
2728      * Event that fires prior to the onDrag event.  Overrides
2729      * Roo.dd.DragDrop.
2730      */
2731     b4Drag: function(e) {
2732         this.setDragElPos(e.getPageX(),
2733                             e.getPageY());
2734     },
2735
2736     toString: function() {
2737         return ("DD " + this.id);
2738     }
2739
2740     //////////////////////////////////////////////////////////////////////////
2741     // Debugging ygDragDrop events that can be overridden
2742     //////////////////////////////////////////////////////////////////////////
2743     /*
2744     startDrag: function(x, y) {
2745     },
2746
2747     onDrag: function(e) {
2748     },
2749
2750     onDragEnter: function(e, id) {
2751     },
2752
2753     onDragOver: function(e, id) {
2754     },
2755
2756     onDragOut: function(e, id) {
2757     },
2758
2759     onDragDrop: function(e, id) {
2760     },
2761
2762     endDrag: function(e) {
2763     }
2764
2765     */
2766
2767 });/*
2768  * Based on:
2769  * Ext JS Library 1.1.1
2770  * Copyright(c) 2006-2007, Ext JS, LLC.
2771  *
2772  * Originally Released Under LGPL - original licence link has changed is not relivant.
2773  *
2774  * Fork - LGPL
2775  * <script type="text/javascript">
2776  */
2777
2778 /**
2779  * @class Roo.dd.DDProxy
2780  * A DragDrop implementation that inserts an empty, bordered div into
2781  * the document that follows the cursor during drag operations.  At the time of
2782  * the click, the frame div is resized to the dimensions of the linked html
2783  * element, and moved to the exact location of the linked element.
2784  *
2785  * References to the "frame" element refer to the single proxy element that
2786  * was created to be dragged in place of all DDProxy elements on the
2787  * page.
2788  *
2789  * @extends Roo.dd.DD
2790  * @constructor
2791  * @param {String} id the id of the linked html element
2792  * @param {String} sGroup the group of related DragDrop objects
2793  * @param {object} config an object containing configurable attributes
2794  *                Valid properties for DDProxy in addition to those in DragDrop:
2795  *                   resizeFrame, centerFrame, dragElId
2796  */
2797 Roo.dd.DDProxy = function(id, sGroup, config) {
2798     if (id) {
2799         this.init(id, sGroup, config);
2800         this.initFrame();
2801     }
2802 };
2803
2804 /**
2805  * The default drag frame div id
2806  * @property Roo.dd.DDProxy.dragElId
2807  * @type String
2808  * @static
2809  */
2810 Roo.dd.DDProxy.dragElId = "ygddfdiv";
2811
2812 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
2813
2814     /**
2815      * By default we resize the drag frame to be the same size as the element
2816      * we want to drag (this is to get the frame effect).  We can turn it off
2817      * if we want a different behavior.
2818      * @property resizeFrame
2819      * @type boolean
2820      */
2821     resizeFrame: true,
2822
2823     /**
2824      * By default the frame is positioned exactly where the drag element is, so
2825      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
2826      * you do not have constraints on the obj is to have the drag frame centered
2827      * around the cursor.  Set centerFrame to true for this effect.
2828      * @property centerFrame
2829      * @type boolean
2830      */
2831     centerFrame: false,
2832
2833     /**
2834      * Creates the proxy element if it does not yet exist
2835      * @method createFrame
2836      */
2837     createFrame: function() {
2838         var self = this;
2839         var body = document.body;
2840
2841         if (!body || !body.firstChild) {
2842             setTimeout( function() { self.createFrame(); }, 50 );
2843             return;
2844         }
2845
2846         var div = this.getDragEl();
2847
2848         if (!div) {
2849             div    = document.createElement("div");
2850             div.id = this.dragElId;
2851             var s  = div.style;
2852
2853             s.position   = "absolute";
2854             s.visibility = "hidden";
2855             s.cursor     = "move";
2856             s.border     = "2px solid #aaa";
2857             s.zIndex     = 999;
2858
2859             // appendChild can blow up IE if invoked prior to the window load event
2860             // while rendering a table.  It is possible there are other scenarios
2861             // that would cause this to happen as well.
2862             body.insertBefore(div, body.firstChild);
2863         }
2864     },
2865
2866     /**
2867      * Initialization for the drag frame element.  Must be called in the
2868      * constructor of all subclasses
2869      * @method initFrame
2870      */
2871     initFrame: function() {
2872         this.createFrame();
2873     },
2874
2875     applyConfig: function() {
2876         Roo.dd.DDProxy.superclass.applyConfig.call(this);
2877
2878         this.resizeFrame = (this.config.resizeFrame !== false);
2879         this.centerFrame = (this.config.centerFrame);
2880         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
2881     },
2882
2883     /**
2884      * Resizes the drag frame to the dimensions of the clicked object, positions
2885      * it over the object, and finally displays it
2886      * @method showFrame
2887      * @param {int} iPageX X click position
2888      * @param {int} iPageY Y click position
2889      * @private
2890      */
2891     showFrame: function(iPageX, iPageY) {
2892         var el = this.getEl();
2893         var dragEl = this.getDragEl();
2894         var s = dragEl.style;
2895
2896         this._resizeProxy();
2897
2898         if (this.centerFrame) {
2899             this.setDelta( Math.round(parseInt(s.width,  10)/2),
2900                            Math.round(parseInt(s.height, 10)/2) );
2901         }
2902
2903         this.setDragElPos(iPageX, iPageY);
2904
2905         Roo.fly(dragEl).show();
2906     },
2907
2908     /**
2909      * The proxy is automatically resized to the dimensions of the linked
2910      * element when a drag is initiated, unless resizeFrame is set to false
2911      * @method _resizeProxy
2912      * @private
2913      */
2914     _resizeProxy: function() {
2915         if (this.resizeFrame) {
2916             var el = this.getEl();
2917             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
2918         }
2919     },
2920
2921     // overrides Roo.dd.DragDrop
2922     b4MouseDown: function(e) {
2923         var x = e.getPageX();
2924         var y = e.getPageY();
2925         this.autoOffset(x, y);
2926         this.setDragElPos(x, y);
2927     },
2928
2929     // overrides Roo.dd.DragDrop
2930     b4StartDrag: function(x, y) {
2931         // show the drag frame
2932         this.showFrame(x, y);
2933     },
2934
2935     // overrides Roo.dd.DragDrop
2936     b4EndDrag: function(e) {
2937         Roo.fly(this.getDragEl()).hide();
2938     },
2939
2940     // overrides Roo.dd.DragDrop
2941     // By default we try to move the element to the last location of the frame.
2942     // This is so that the default behavior mirrors that of Roo.dd.DD.
2943     endDrag: function(e) {
2944
2945         var lel = this.getEl();
2946         var del = this.getDragEl();
2947
2948         // Show the drag frame briefly so we can get its position
2949         del.style.visibility = "";
2950
2951         this.beforeMove();
2952         // Hide the linked element before the move to get around a Safari
2953         // rendering bug.
2954         lel.style.visibility = "hidden";
2955         Roo.dd.DDM.moveToEl(lel, del);
2956         del.style.visibility = "hidden";
2957         lel.style.visibility = "";
2958
2959         this.afterDrag();
2960     },
2961
2962     beforeMove : function(){
2963
2964     },
2965
2966     afterDrag : function(){
2967
2968     },
2969
2970     toString: function() {
2971         return ("DDProxy " + this.id);
2972     }
2973
2974 });
2975 /*
2976  * Based on:
2977  * Ext JS Library 1.1.1
2978  * Copyright(c) 2006-2007, Ext JS, LLC.
2979  *
2980  * Originally Released Under LGPL - original licence link has changed is not relivant.
2981  *
2982  * Fork - LGPL
2983  * <script type="text/javascript">
2984  */
2985
2986  /**
2987  * @class Roo.dd.DDTarget
2988  * A DragDrop implementation that does not move, but can be a drop
2989  * target.  You would get the same result by simply omitting implementation
2990  * for the event callbacks, but this way we reduce the processing cost of the
2991  * event listener and the callbacks.
2992  * @extends Roo.dd.DragDrop
2993  * @constructor
2994  * @param {String} id the id of the element that is a drop target
2995  * @param {String} sGroup the group of related DragDrop objects
2996  * @param {object} config an object containing configurable attributes
2997  *                 Valid properties for DDTarget in addition to those in
2998  *                 DragDrop:
2999  *                    none
3000  */
3001 Roo.dd.DDTarget = function(id, sGroup, config) {
3002     if (id) {
3003         this.initTarget(id, sGroup, config);
3004     }
3005     if (config.listeners || config.events) { 
3006        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
3007             listeners : config.listeners || {}, 
3008             events : config.events || {} 
3009         });    
3010     }
3011 };
3012
3013 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
3014 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
3015     toString: function() {
3016         return ("DDTarget " + this.id);
3017     }
3018 });
3019 /*
3020  * Based on:
3021  * Ext JS Library 1.1.1
3022  * Copyright(c) 2006-2007, Ext JS, LLC.
3023  *
3024  * Originally Released Under LGPL - original licence link has changed is not relivant.
3025  *
3026  * Fork - LGPL
3027  * <script type="text/javascript">
3028  */
3029  
3030
3031 /**
3032  * @class Roo.dd.ScrollManager
3033  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
3034  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
3035  * @singleton
3036  */
3037 Roo.dd.ScrollManager = function(){
3038     var ddm = Roo.dd.DragDropMgr;
3039     var els = {};
3040     var dragEl = null;
3041     var proc = {};
3042     
3043     
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         /**
13532          * @event resize
13533          * Fired after a resize.
13534          * @param {Roo.Resizable} this
13535          * @param {Number} width The new width
13536          * @param {Number} height The new height
13537          * @param {Roo.EventObject} e The mouseup event
13538          */
13539         "resize" : true
13540     });
13541
13542     if(this.width !== null && this.height !== null){
13543         this.resizeTo(this.width, this.height);
13544     }else{
13545         this.updateChildSize();
13546     }
13547     if(Roo.isIE){
13548         this.el.dom.style.zoom = 1;
13549     }
13550     Roo.Resizable.superclass.constructor.call(this);
13551 };
13552
13553 Roo.extend(Roo.Resizable, Roo.util.Observable, {
13554         resizeChild : false,
13555         adjustments : [0, 0],
13556         minWidth : 5,
13557         minHeight : 5,
13558         maxWidth : 10000,
13559         maxHeight : 10000,
13560         enabled : true,
13561         animate : false,
13562         duration : .35,
13563         dynamic : false,
13564         handles : false,
13565         multiDirectional : false,
13566         disableTrackOver : false,
13567         easing : 'easeOutStrong',
13568         widthIncrement : 0,
13569         heightIncrement : 0,
13570         pinned : false,
13571         width : null,
13572         height : null,
13573         preserveRatio : false,
13574         transparent: false,
13575         minX: 0,
13576         minY: 0,
13577         draggable: false,
13578
13579         /**
13580          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
13581          */
13582         constrainTo: undefined,
13583         /**
13584          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
13585          */
13586         resizeRegion: undefined,
13587
13588
13589     /**
13590      * Perform a manual resize
13591      * @param {Number} width
13592      * @param {Number} height
13593      */
13594     resizeTo : function(width, height){
13595         this.el.setSize(width, height);
13596         this.updateChildSize();
13597         this.fireEvent("resize", this, width, height, null);
13598     },
13599
13600     // private
13601     startSizing : function(e, handle){
13602         this.fireEvent("beforeresize", this, e);
13603         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
13604
13605             if(!this.overlay){
13606                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
13607                 this.overlay.unselectable();
13608                 this.overlay.enableDisplayMode("block");
13609                 this.overlay.on("mousemove", this.onMouseMove, this);
13610                 this.overlay.on("mouseup", this.onMouseUp, this);
13611             }
13612             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
13613
13614             this.resizing = true;
13615             this.startBox = this.el.getBox();
13616             this.startPoint = e.getXY();
13617             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
13618                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
13619
13620             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13621             this.overlay.show();
13622
13623             if(this.constrainTo) {
13624                 var ct = Roo.get(this.constrainTo);
13625                 this.resizeRegion = ct.getRegion().adjust(
13626                     ct.getFrameWidth('t'),
13627                     ct.getFrameWidth('l'),
13628                     -ct.getFrameWidth('b'),
13629                     -ct.getFrameWidth('r')
13630                 );
13631             }
13632
13633             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
13634             this.proxy.show();
13635             this.proxy.setBox(this.startBox);
13636             if(!this.dynamic){
13637                 this.proxy.setStyle('visibility', 'visible');
13638             }
13639         }
13640     },
13641
13642     // private
13643     onMouseDown : function(handle, e){
13644         if(this.enabled){
13645             e.stopEvent();
13646             this.activeHandle = handle;
13647             this.startSizing(e, handle);
13648         }
13649     },
13650
13651     // private
13652     onMouseUp : function(e){
13653         var size = this.resizeElement();
13654         this.resizing = false;
13655         this.handleOut();
13656         this.overlay.hide();
13657         this.proxy.hide();
13658         this.fireEvent("resize", this, size.width, size.height, e);
13659     },
13660
13661     // private
13662     updateChildSize : function(){
13663         
13664         if(this.resizeChild){
13665             var el = this.el;
13666             var child = this.resizeChild;
13667             var adj = this.adjustments;
13668             if(el.dom.offsetWidth){
13669                 var b = el.getSize(true);
13670                 child.setSize(b.width+adj[0], b.height+adj[1]);
13671             }
13672             // Second call here for IE
13673             // The first call enables instant resizing and
13674             // the second call corrects scroll bars if they
13675             // exist
13676             if(Roo.isIE){
13677                 setTimeout(function(){
13678                     if(el.dom.offsetWidth){
13679                         var b = el.getSize(true);
13680                         child.setSize(b.width+adj[0], b.height+adj[1]);
13681                     }
13682                 }, 10);
13683             }
13684         }
13685     },
13686
13687     // private
13688     snap : function(value, inc, min){
13689         if(!inc || !value) return value;
13690         var newValue = value;
13691         var m = value % inc;
13692         if(m > 0){
13693             if(m > (inc/2)){
13694                 newValue = value + (inc-m);
13695             }else{
13696                 newValue = value - m;
13697             }
13698         }
13699         return Math.max(min, newValue);
13700     },
13701
13702     // private
13703     resizeElement : function(){
13704         var box = this.proxy.getBox();
13705         if(this.updateBox){
13706             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13707         }else{
13708             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13709         }
13710         this.updateChildSize();
13711         if(!this.dynamic){
13712             this.proxy.hide();
13713         }
13714         return box;
13715     },
13716
13717     // private
13718     constrain : function(v, diff, m, mx){
13719         if(v - diff < m){
13720             diff = v - m;
13721         }else if(v - diff > mx){
13722             diff = mx - v;
13723         }
13724         return diff;
13725     },
13726
13727     // private
13728     onMouseMove : function(e){
13729         if(this.enabled){
13730             try{// try catch so if something goes wrong the user doesn't get hung
13731
13732             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13733                 return;
13734             }
13735
13736             //var curXY = this.startPoint;
13737             var curSize = this.curSize || this.startBox;
13738             var x = this.startBox.x, y = this.startBox.y;
13739             var ox = x, oy = y;
13740             var w = curSize.width, h = curSize.height;
13741             var ow = w, oh = h;
13742             var mw = this.minWidth, mh = this.minHeight;
13743             var mxw = this.maxWidth, mxh = this.maxHeight;
13744             var wi = this.widthIncrement;
13745             var hi = this.heightIncrement;
13746
13747             var eventXY = e.getXY();
13748             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13749             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13750
13751             var pos = this.activeHandle.position;
13752
13753             switch(pos){
13754                 case "east":
13755                     w += diffX;
13756                     w = Math.min(Math.max(mw, w), mxw);
13757                     break;
13758              
13759                 case "south":
13760                     h += diffY;
13761                     h = Math.min(Math.max(mh, h), mxh);
13762                     break;
13763                 case "southeast":
13764                     w += diffX;
13765                     h += diffY;
13766                     w = Math.min(Math.max(mw, w), mxw);
13767                     h = Math.min(Math.max(mh, h), mxh);
13768                     break;
13769                 case "north":
13770                     diffY = this.constrain(h, diffY, mh, mxh);
13771                     y += diffY;
13772                     h -= diffY;
13773                     break;
13774                 case "hdrag":
13775                     
13776                     if (wi) {
13777                         var adiffX = Math.abs(diffX);
13778                         var sub = (adiffX % wi); // how much 
13779                         if (sub > (wi/2)) { // far enough to snap
13780                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
13781                         } else {
13782                             // remove difference.. 
13783                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
13784                         }
13785                     }
13786                     x += diffX;
13787                     x = Math.max(this.minX, x);
13788                     break;
13789                 case "west":
13790                     diffX = this.constrain(w, diffX, mw, mxw);
13791                     x += diffX;
13792                     w -= diffX;
13793                     break;
13794                 case "northeast":
13795                     w += diffX;
13796                     w = Math.min(Math.max(mw, w), mxw);
13797                     diffY = this.constrain(h, diffY, mh, mxh);
13798                     y += diffY;
13799                     h -= diffY;
13800                     break;
13801                 case "northwest":
13802                     diffX = this.constrain(w, diffX, mw, mxw);
13803                     diffY = this.constrain(h, diffY, mh, mxh);
13804                     y += diffY;
13805                     h -= diffY;
13806                     x += diffX;
13807                     w -= diffX;
13808                     break;
13809                case "southwest":
13810                     diffX = this.constrain(w, diffX, mw, mxw);
13811                     h += diffY;
13812                     h = Math.min(Math.max(mh, h), mxh);
13813                     x += diffX;
13814                     w -= diffX;
13815                     break;
13816             }
13817
13818             var sw = this.snap(w, wi, mw);
13819             var sh = this.snap(h, hi, mh);
13820             if(sw != w || sh != h){
13821                 switch(pos){
13822                     case "northeast":
13823                         y -= sh - h;
13824                     break;
13825                     case "north":
13826                         y -= sh - h;
13827                         break;
13828                     case "southwest":
13829                         x -= sw - w;
13830                     break;
13831                     case "west":
13832                         x -= sw - w;
13833                         break;
13834                     case "northwest":
13835                         x -= sw - w;
13836                         y -= sh - h;
13837                     break;
13838                 }
13839                 w = sw;
13840                 h = sh;
13841             }
13842
13843             if(this.preserveRatio){
13844                 switch(pos){
13845                     case "southeast":
13846                     case "east":
13847                         h = oh * (w/ow);
13848                         h = Math.min(Math.max(mh, h), mxh);
13849                         w = ow * (h/oh);
13850                        break;
13851                     case "south":
13852                         w = ow * (h/oh);
13853                         w = Math.min(Math.max(mw, w), mxw);
13854                         h = oh * (w/ow);
13855                         break;
13856                     case "northeast":
13857                         w = ow * (h/oh);
13858                         w = Math.min(Math.max(mw, w), mxw);
13859                         h = oh * (w/ow);
13860                     break;
13861                     case "north":
13862                         var tw = w;
13863                         w = ow * (h/oh);
13864                         w = Math.min(Math.max(mw, w), mxw);
13865                         h = oh * (w/ow);
13866                         x += (tw - w) / 2;
13867                         break;
13868                     case "southwest":
13869                         h = oh * (w/ow);
13870                         h = Math.min(Math.max(mh, h), mxh);
13871                         var tw = w;
13872                         w = ow * (h/oh);
13873                         x += tw - w;
13874                         break;
13875                     case "west":
13876                         var th = h;
13877                         h = oh * (w/ow);
13878                         h = Math.min(Math.max(mh, h), mxh);
13879                         y += (th - h) / 2;
13880                         var tw = w;
13881                         w = ow * (h/oh);
13882                         x += tw - w;
13883                        break;
13884                     case "northwest":
13885                         var tw = w;
13886                         var th = h;
13887                         h = oh * (w/ow);
13888                         h = Math.min(Math.max(mh, h), mxh);
13889                         w = ow * (h/oh);
13890                         y += th - h;
13891                         x += tw - w;
13892                        break;
13893
13894                 }
13895             }
13896             if (pos == 'hdrag') {
13897                 w = ow;
13898             }
13899             this.proxy.setBounds(x, y, w, h);
13900             if(this.dynamic){
13901                 this.resizeElement();
13902             }
13903             }catch(e){}
13904         }
13905     },
13906
13907     // private
13908     handleOver : function(){
13909         if(this.enabled){
13910             this.el.addClass("x-resizable-over");
13911         }
13912     },
13913
13914     // private
13915     handleOut : function(){
13916         if(!this.resizing){
13917             this.el.removeClass("x-resizable-over");
13918         }
13919     },
13920
13921     /**
13922      * Returns the element this component is bound to.
13923      * @return {Roo.Element}
13924      */
13925     getEl : function(){
13926         return this.el;
13927     },
13928
13929     /**
13930      * Returns the resizeChild element (or null).
13931      * @return {Roo.Element}
13932      */
13933     getResizeChild : function(){
13934         return this.resizeChild;
13935     },
13936     groupHandler : function()
13937     {
13938         
13939     },
13940     /**
13941      * Destroys this resizable. If the element was wrapped and
13942      * removeEl is not true then the element remains.
13943      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13944      */
13945     destroy : function(removeEl){
13946         this.proxy.remove();
13947         if(this.overlay){
13948             this.overlay.removeAllListeners();
13949             this.overlay.remove();
13950         }
13951         var ps = Roo.Resizable.positions;
13952         for(var k in ps){
13953             if(typeof ps[k] != "function" && this[ps[k]]){
13954                 var h = this[ps[k]];
13955                 h.el.removeAllListeners();
13956                 h.el.remove();
13957             }
13958         }
13959         if(removeEl){
13960             this.el.update("");
13961             this.el.remove();
13962         }
13963     }
13964 });
13965
13966 // private
13967 // hash to map config positions to true positions
13968 Roo.Resizable.positions = {
13969     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
13970     hd: "hdrag"
13971 };
13972
13973 // private
13974 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
13975     if(!this.tpl){
13976         // only initialize the template if resizable is used
13977         var tpl = Roo.DomHelper.createTemplate(
13978             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
13979         );
13980         tpl.compile();
13981         Roo.Resizable.Handle.prototype.tpl = tpl;
13982     }
13983     this.position = pos;
13984     this.rz = rz;
13985     // show north drag fro topdra
13986     var handlepos = pos == 'hdrag' ? 'north' : pos;
13987     
13988     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
13989     if (pos == 'hdrag') {
13990         this.el.setStyle('cursor', 'pointer');
13991     }
13992     this.el.unselectable();
13993     if(transparent){
13994         this.el.setOpacity(0);
13995     }
13996     this.el.on("mousedown", this.onMouseDown, this);
13997     if(!disableTrackOver){
13998         this.el.on("mouseover", this.onMouseOver, this);
13999         this.el.on("mouseout", this.onMouseOut, this);
14000     }
14001 };
14002
14003 // private
14004 Roo.Resizable.Handle.prototype = {
14005     afterResize : function(rz){
14006         // do nothing
14007     },
14008     // private
14009     onMouseDown : function(e){
14010         this.rz.onMouseDown(this, e);
14011     },
14012     // private
14013     onMouseOver : function(e){
14014         this.rz.handleOver(this, e);
14015     },
14016     // private
14017     onMouseOut : function(e){
14018         this.rz.handleOut(this, e);
14019     }
14020 };/*
14021  * Based on:
14022  * Ext JS Library 1.1.1
14023  * Copyright(c) 2006-2007, Ext JS, LLC.
14024  *
14025  * Originally Released Under LGPL - original licence link has changed is not relivant.
14026  *
14027  * Fork - LGPL
14028  * <script type="text/javascript">
14029  */
14030
14031 /**
14032  * @class Roo.Editor
14033  * @extends Roo.Component
14034  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
14035  * @constructor
14036  * Create a new Editor
14037  * @param {Roo.form.Field} field The Field object (or descendant)
14038  * @param {Object} config The config object
14039  */
14040 Roo.Editor = function(field, config){
14041     Roo.Editor.superclass.constructor.call(this, config);
14042     this.field = field;
14043     this.addEvents({
14044         /**
14045              * @event beforestartedit
14046              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
14047              * false from the handler of this event.
14048              * @param {Editor} this
14049              * @param {Roo.Element} boundEl The underlying element bound to this editor
14050              * @param {Mixed} value The field value being set
14051              */
14052         "beforestartedit" : true,
14053         /**
14054              * @event startedit
14055              * Fires when this editor is displayed
14056              * @param {Roo.Element} boundEl The underlying element bound to this editor
14057              * @param {Mixed} value The starting field value
14058              */
14059         "startedit" : true,
14060         /**
14061              * @event beforecomplete
14062              * Fires after a change has been made to the field, but before the change is reflected in the underlying
14063              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
14064              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
14065              * event will not fire since no edit actually occurred.
14066              * @param {Editor} this
14067              * @param {Mixed} value The current field value
14068              * @param {Mixed} startValue The original field value
14069              */
14070         "beforecomplete" : true,
14071         /**
14072              * @event complete
14073              * Fires after editing is complete and any changed value has been written to the underlying field.
14074              * @param {Editor} this
14075              * @param {Mixed} value The current field value
14076              * @param {Mixed} startValue The original field value
14077              */
14078         "complete" : true,
14079         /**
14080          * @event specialkey
14081          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
14082          * {@link Roo.EventObject#getKey} to determine which key was pressed.
14083          * @param {Roo.form.Field} this
14084          * @param {Roo.EventObject} e The event object
14085          */
14086         "specialkey" : true
14087     });
14088 };
14089
14090 Roo.extend(Roo.Editor, Roo.Component, {
14091     /**
14092      * @cfg {Boolean/String} autosize
14093      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
14094      * or "height" to adopt the height only (defaults to false)
14095      */
14096     /**
14097      * @cfg {Boolean} revertInvalid
14098      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
14099      * validation fails (defaults to true)
14100      */
14101     /**
14102      * @cfg {Boolean} ignoreNoChange
14103      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
14104      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
14105      * will never be ignored.
14106      */
14107     /**
14108      * @cfg {Boolean} hideEl
14109      * False to keep the bound element visible while the editor is displayed (defaults to true)
14110      */
14111     /**
14112      * @cfg {Mixed} value
14113      * The data value of the underlying field (defaults to "")
14114      */
14115     value : "",
14116     /**
14117      * @cfg {String} alignment
14118      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
14119      */
14120     alignment: "c-c?",
14121     /**
14122      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
14123      * for bottom-right shadow (defaults to "frame")
14124      */
14125     shadow : "frame",
14126     /**
14127      * @cfg {Boolean} constrain True to constrain the editor to the viewport
14128      */
14129     constrain : false,
14130     /**
14131      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
14132      */
14133     completeOnEnter : false,
14134     /**
14135      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
14136      */
14137     cancelOnEsc : false,
14138     /**
14139      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
14140      */
14141     updateEl : false,
14142
14143     // private
14144     onRender : function(ct, position){
14145         this.el = new Roo.Layer({
14146             shadow: this.shadow,
14147             cls: "x-editor",
14148             parentEl : ct,
14149             shim : this.shim,
14150             shadowOffset:4,
14151             id: this.id,
14152             constrain: this.constrain
14153         });
14154         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
14155         if(this.field.msgTarget != 'title'){
14156             this.field.msgTarget = 'qtip';
14157         }
14158         this.field.render(this.el);
14159         if(Roo.isGecko){
14160             this.field.el.dom.setAttribute('autocomplete', 'off');
14161         }
14162         this.field.on("specialkey", this.onSpecialKey, this);
14163         if(this.swallowKeys){
14164             this.field.el.swallowEvent(['keydown','keypress']);
14165         }
14166         this.field.show();
14167         this.field.on("blur", this.onBlur, this);
14168         if(this.field.grow){
14169             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
14170         }
14171     },
14172
14173     onSpecialKey : function(field, e)
14174     {
14175         //Roo.log('editor onSpecialKey');
14176         if(this.completeOnEnter && e.getKey() == e.ENTER){
14177             e.stopEvent();
14178             this.completeEdit();
14179             return;
14180         }
14181         // do not fire special key otherwise it might hide close the editor...
14182         if(e.getKey() == e.ENTER){    
14183             return;
14184         }
14185         if(this.cancelOnEsc && e.getKey() == e.ESC){
14186             this.cancelEdit();
14187             return;
14188         } 
14189         this.fireEvent('specialkey', field, e);
14190     
14191     },
14192
14193     /**
14194      * Starts the editing process and shows the editor.
14195      * @param {String/HTMLElement/Element} el The element to edit
14196      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
14197       * to the innerHTML of el.
14198      */
14199     startEdit : function(el, value){
14200         if(this.editing){
14201             this.completeEdit();
14202         }
14203         this.boundEl = Roo.get(el);
14204         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
14205         if(!this.rendered){
14206             this.render(this.parentEl || document.body);
14207         }
14208         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
14209             return;
14210         }
14211         this.startValue = v;
14212         this.field.setValue(v);
14213         if(this.autoSize){
14214             var sz = this.boundEl.getSize();
14215             switch(this.autoSize){
14216                 case "width":
14217                 this.setSize(sz.width,  "");
14218                 break;
14219                 case "height":
14220                 this.setSize("",  sz.height);
14221                 break;
14222                 default:
14223                 this.setSize(sz.width,  sz.height);
14224             }
14225         }
14226         this.el.alignTo(this.boundEl, this.alignment);
14227         this.editing = true;
14228         if(Roo.QuickTips){
14229             Roo.QuickTips.disable();
14230         }
14231         this.show();
14232     },
14233
14234     /**
14235      * Sets the height and width of this editor.
14236      * @param {Number} width The new width
14237      * @param {Number} height The new height
14238      */
14239     setSize : function(w, h){
14240         this.field.setSize(w, h);
14241         if(this.el){
14242             this.el.sync();
14243         }
14244     },
14245
14246     /**
14247      * Realigns the editor to the bound field based on the current alignment config value.
14248      */
14249     realign : function(){
14250         this.el.alignTo(this.boundEl, this.alignment);
14251     },
14252
14253     /**
14254      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
14255      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
14256      */
14257     completeEdit : function(remainVisible){
14258         if(!this.editing){
14259             return;
14260         }
14261         var v = this.getValue();
14262         if(this.revertInvalid !== false && !this.field.isValid()){
14263             v = this.startValue;
14264             this.cancelEdit(true);
14265         }
14266         if(String(v) === String(this.startValue) && this.ignoreNoChange){
14267             this.editing = false;
14268             this.hide();
14269             return;
14270         }
14271         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
14272             this.editing = false;
14273             if(this.updateEl && this.boundEl){
14274                 this.boundEl.update(v);
14275             }
14276             if(remainVisible !== true){
14277                 this.hide();
14278             }
14279             this.fireEvent("complete", this, v, this.startValue);
14280         }
14281     },
14282
14283     // private
14284     onShow : function(){
14285         this.el.show();
14286         if(this.hideEl !== false){
14287             this.boundEl.hide();
14288         }
14289         this.field.show();
14290         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
14291             this.fixIEFocus = true;
14292             this.deferredFocus.defer(50, this);
14293         }else{
14294             this.field.focus();
14295         }
14296         this.fireEvent("startedit", this.boundEl, this.startValue);
14297     },
14298
14299     deferredFocus : function(){
14300         if(this.editing){
14301             this.field.focus();
14302         }
14303     },
14304
14305     /**
14306      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
14307      * reverted to the original starting value.
14308      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
14309      * cancel (defaults to false)
14310      */
14311     cancelEdit : function(remainVisible){
14312         if(this.editing){
14313             this.setValue(this.startValue);
14314             if(remainVisible !== true){
14315                 this.hide();
14316             }
14317         }
14318     },
14319
14320     // private
14321     onBlur : function(){
14322         if(this.allowBlur !== true && this.editing){
14323             this.completeEdit();
14324         }
14325     },
14326
14327     // private
14328     onHide : function(){
14329         if(this.editing){
14330             this.completeEdit();
14331             return;
14332         }
14333         this.field.blur();
14334         if(this.field.collapse){
14335             this.field.collapse();
14336         }
14337         this.el.hide();
14338         if(this.hideEl !== false){
14339             this.boundEl.show();
14340         }
14341         if(Roo.QuickTips){
14342             Roo.QuickTips.enable();
14343         }
14344     },
14345
14346     /**
14347      * Sets the data value of the editor
14348      * @param {Mixed} value Any valid value supported by the underlying field
14349      */
14350     setValue : function(v){
14351         this.field.setValue(v);
14352     },
14353
14354     /**
14355      * Gets the data value of the editor
14356      * @return {Mixed} The data value
14357      */
14358     getValue : function(){
14359         return this.field.getValue();
14360     }
14361 });/*
14362  * Based on:
14363  * Ext JS Library 1.1.1
14364  * Copyright(c) 2006-2007, Ext JS, LLC.
14365  *
14366  * Originally Released Under LGPL - original licence link has changed is not relivant.
14367  *
14368  * Fork - LGPL
14369  * <script type="text/javascript">
14370  */
14371  
14372 /**
14373  * @class Roo.BasicDialog
14374  * @extends Roo.util.Observable
14375  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
14376  * <pre><code>
14377 var dlg = new Roo.BasicDialog("my-dlg", {
14378     height: 200,
14379     width: 300,
14380     minHeight: 100,
14381     minWidth: 150,
14382     modal: true,
14383     proxyDrag: true,
14384     shadow: true
14385 });
14386 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
14387 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
14388 dlg.addButton('Cancel', dlg.hide, dlg);
14389 dlg.show();
14390 </code></pre>
14391   <b>A Dialog should always be a direct child of the body element.</b>
14392  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
14393  * @cfg {String} title Default text to display in the title bar (defaults to null)
14394  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14395  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14396  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
14397  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
14398  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
14399  * (defaults to null with no animation)
14400  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
14401  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
14402  * property for valid values (defaults to 'all')
14403  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
14404  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
14405  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
14406  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
14407  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
14408  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
14409  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
14410  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
14411  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
14412  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
14413  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
14414  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
14415  * draggable = true (defaults to false)
14416  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
14417  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14418  * shadow (defaults to false)
14419  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
14420  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
14421  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
14422  * @cfg {Array} buttons Array of buttons
14423  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
14424  * @constructor
14425  * Create a new BasicDialog.
14426  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
14427  * @param {Object} config Configuration options
14428  */
14429 Roo.BasicDialog = function(el, config){
14430     this.el = Roo.get(el);
14431     var dh = Roo.DomHelper;
14432     if(!this.el && config && config.autoCreate){
14433         if(typeof config.autoCreate == "object"){
14434             if(!config.autoCreate.id){
14435                 config.autoCreate.id = el;
14436             }
14437             this.el = dh.append(document.body,
14438                         config.autoCreate, true);
14439         }else{
14440             this.el = dh.append(document.body,
14441                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
14442         }
14443     }
14444     el = this.el;
14445     el.setDisplayed(true);
14446     el.hide = this.hideAction;
14447     this.id = el.id;
14448     el.addClass("x-dlg");
14449
14450     Roo.apply(this, config);
14451
14452     this.proxy = el.createProxy("x-dlg-proxy");
14453     this.proxy.hide = this.hideAction;
14454     this.proxy.setOpacity(.5);
14455     this.proxy.hide();
14456
14457     if(config.width){
14458         el.setWidth(config.width);
14459     }
14460     if(config.height){
14461         el.setHeight(config.height);
14462     }
14463     this.size = el.getSize();
14464     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
14465         this.xy = [config.x,config.y];
14466     }else{
14467         this.xy = el.getCenterXY(true);
14468     }
14469     /** The header element @type Roo.Element */
14470     this.header = el.child("> .x-dlg-hd");
14471     /** The body element @type Roo.Element */
14472     this.body = el.child("> .x-dlg-bd");
14473     /** The footer element @type Roo.Element */
14474     this.footer = el.child("> .x-dlg-ft");
14475
14476     if(!this.header){
14477         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
14478     }
14479     if(!this.body){
14480         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
14481     }
14482
14483     this.header.unselectable();
14484     if(this.title){
14485         this.header.update(this.title);
14486     }
14487     // this element allows the dialog to be focused for keyboard event
14488     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
14489     this.focusEl.swallowEvent("click", true);
14490
14491     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
14492
14493     // wrap the body and footer for special rendering
14494     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
14495     if(this.footer){
14496         this.bwrap.dom.appendChild(this.footer.dom);
14497     }
14498
14499     this.bg = this.el.createChild({
14500         tag: "div", cls:"x-dlg-bg",
14501         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
14502     });
14503     this.centerBg = this.bg.child("div.x-dlg-bg-center");
14504
14505
14506     if(this.autoScroll !== false && !this.autoTabs){
14507         this.body.setStyle("overflow", "auto");
14508     }
14509
14510     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
14511
14512     if(this.closable !== false){
14513         this.el.addClass("x-dlg-closable");
14514         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
14515         this.close.on("click", this.closeClick, this);
14516         this.close.addClassOnOver("x-dlg-close-over");
14517     }
14518     if(this.collapsible !== false){
14519         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
14520         this.collapseBtn.on("click", this.collapseClick, this);
14521         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
14522         this.header.on("dblclick", this.collapseClick, this);
14523     }
14524     if(this.resizable !== false){
14525         this.el.addClass("x-dlg-resizable");
14526         this.resizer = new Roo.Resizable(el, {
14527             minWidth: this.minWidth || 80,
14528             minHeight:this.minHeight || 80,
14529             handles: this.resizeHandles || "all",
14530             pinned: true
14531         });
14532         this.resizer.on("beforeresize", this.beforeResize, this);
14533         this.resizer.on("resize", this.onResize, this);
14534     }
14535     if(this.draggable !== false){
14536         el.addClass("x-dlg-draggable");
14537         if (!this.proxyDrag) {
14538             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
14539         }
14540         else {
14541             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
14542         }
14543         dd.setHandleElId(this.header.id);
14544         dd.endDrag = this.endMove.createDelegate(this);
14545         dd.startDrag = this.startMove.createDelegate(this);
14546         dd.onDrag = this.onDrag.createDelegate(this);
14547         dd.scroll = false;
14548         this.dd = dd;
14549     }
14550     if(this.modal){
14551         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
14552         this.mask.enableDisplayMode("block");
14553         this.mask.hide();
14554         this.el.addClass("x-dlg-modal");
14555     }
14556     if(this.shadow){
14557         this.shadow = new Roo.Shadow({
14558             mode : typeof this.shadow == "string" ? this.shadow : "sides",
14559             offset : this.shadowOffset
14560         });
14561     }else{
14562         this.shadowOffset = 0;
14563     }
14564     if(Roo.useShims && this.shim !== false){
14565         this.shim = this.el.createShim();
14566         this.shim.hide = this.hideAction;
14567         this.shim.hide();
14568     }else{
14569         this.shim = false;
14570     }
14571     if(this.autoTabs){
14572         this.initTabs();
14573     }
14574     if (this.buttons) { 
14575         var bts= this.buttons;
14576         this.buttons = [];
14577         Roo.each(bts, function(b) {
14578             this.addButton(b);
14579         }, this);
14580     }
14581     
14582     
14583     this.addEvents({
14584         /**
14585          * @event keydown
14586          * Fires when a key is pressed
14587          * @param {Roo.BasicDialog} this
14588          * @param {Roo.EventObject} e
14589          */
14590         "keydown" : true,
14591         /**
14592          * @event move
14593          * Fires when this dialog is moved by the user.
14594          * @param {Roo.BasicDialog} this
14595          * @param {Number} x The new page X
14596          * @param {Number} y The new page Y
14597          */
14598         "move" : true,
14599         /**
14600          * @event resize
14601          * Fires when this dialog is resized by the user.
14602          * @param {Roo.BasicDialog} this
14603          * @param {Number} width The new width
14604          * @param {Number} height The new height
14605          */
14606         "resize" : true,
14607         /**
14608          * @event beforehide
14609          * Fires before this dialog is hidden.
14610          * @param {Roo.BasicDialog} this
14611          */
14612         "beforehide" : true,
14613         /**
14614          * @event hide
14615          * Fires when this dialog is hidden.
14616          * @param {Roo.BasicDialog} this
14617          */
14618         "hide" : true,
14619         /**
14620          * @event beforeshow
14621          * Fires before this dialog is shown.
14622          * @param {Roo.BasicDialog} this
14623          */
14624         "beforeshow" : true,
14625         /**
14626          * @event show
14627          * Fires when this dialog is shown.
14628          * @param {Roo.BasicDialog} this
14629          */
14630         "show" : true
14631     });
14632     el.on("keydown", this.onKeyDown, this);
14633     el.on("mousedown", this.toFront, this);
14634     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
14635     this.el.hide();
14636     Roo.DialogManager.register(this);
14637     Roo.BasicDialog.superclass.constructor.call(this);
14638 };
14639
14640 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
14641     shadowOffset: Roo.isIE ? 6 : 5,
14642     minHeight: 80,
14643     minWidth: 200,
14644     minButtonWidth: 75,
14645     defaultButton: null,
14646     buttonAlign: "right",
14647     tabTag: 'div',
14648     firstShow: true,
14649
14650     /**
14651      * Sets the dialog title text
14652      * @param {String} text The title text to display
14653      * @return {Roo.BasicDialog} this
14654      */
14655     setTitle : function(text){
14656         this.header.update(text);
14657         return this;
14658     },
14659
14660     // private
14661     closeClick : function(){
14662         this.hide();
14663     },
14664
14665     // private
14666     collapseClick : function(){
14667         this[this.collapsed ? "expand" : "collapse"]();
14668     },
14669
14670     /**
14671      * Collapses the dialog to its minimized state (only the title bar is visible).
14672      * Equivalent to the user clicking the collapse dialog button.
14673      */
14674     collapse : function(){
14675         if(!this.collapsed){
14676             this.collapsed = true;
14677             this.el.addClass("x-dlg-collapsed");
14678             this.restoreHeight = this.el.getHeight();
14679             this.resizeTo(this.el.getWidth(), this.header.getHeight());
14680         }
14681     },
14682
14683     /**
14684      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
14685      * clicking the expand dialog button.
14686      */
14687     expand : function(){
14688         if(this.collapsed){
14689             this.collapsed = false;
14690             this.el.removeClass("x-dlg-collapsed");
14691             this.resizeTo(this.el.getWidth(), this.restoreHeight);
14692         }
14693     },
14694
14695     /**
14696      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14697      * @return {Roo.TabPanel} The tabs component
14698      */
14699     initTabs : function(){
14700         var tabs = this.getTabs();
14701         while(tabs.getTab(0)){
14702             tabs.removeTab(0);
14703         }
14704         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14705             var dom = el.dom;
14706             tabs.addTab(Roo.id(dom), dom.title);
14707             dom.title = "";
14708         });
14709         tabs.activate(0);
14710         return tabs;
14711     },
14712
14713     // private
14714     beforeResize : function(){
14715         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14716     },
14717
14718     // private
14719     onResize : function(){
14720         this.refreshSize();
14721         this.syncBodyHeight();
14722         this.adjustAssets();
14723         this.focus();
14724         this.fireEvent("resize", this, this.size.width, this.size.height);
14725     },
14726
14727     // private
14728     onKeyDown : function(e){
14729         if(this.isVisible()){
14730             this.fireEvent("keydown", this, e);
14731         }
14732     },
14733
14734     /**
14735      * Resizes the dialog.
14736      * @param {Number} width
14737      * @param {Number} height
14738      * @return {Roo.BasicDialog} this
14739      */
14740     resizeTo : function(width, height){
14741         this.el.setSize(width, height);
14742         this.size = {width: width, height: height};
14743         this.syncBodyHeight();
14744         if(this.fixedcenter){
14745             this.center();
14746         }
14747         if(this.isVisible()){
14748             this.constrainXY();
14749             this.adjustAssets();
14750         }
14751         this.fireEvent("resize", this, width, height);
14752         return this;
14753     },
14754
14755
14756     /**
14757      * Resizes the dialog to fit the specified content size.
14758      * @param {Number} width
14759      * @param {Number} height
14760      * @return {Roo.BasicDialog} this
14761      */
14762     setContentSize : function(w, h){
14763         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14764         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14765         //if(!this.el.isBorderBox()){
14766             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14767             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14768         //}
14769         if(this.tabs){
14770             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14771             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14772         }
14773         this.resizeTo(w, h);
14774         return this;
14775     },
14776
14777     /**
14778      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
14779      * executed in response to a particular key being pressed while the dialog is active.
14780      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14781      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14782      * @param {Function} fn The function to call
14783      * @param {Object} scope (optional) The scope of the function
14784      * @return {Roo.BasicDialog} this
14785      */
14786     addKeyListener : function(key, fn, scope){
14787         var keyCode, shift, ctrl, alt;
14788         if(typeof key == "object" && !(key instanceof Array)){
14789             keyCode = key["key"];
14790             shift = key["shift"];
14791             ctrl = key["ctrl"];
14792             alt = key["alt"];
14793         }else{
14794             keyCode = key;
14795         }
14796         var handler = function(dlg, e){
14797             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14798                 var k = e.getKey();
14799                 if(keyCode instanceof Array){
14800                     for(var i = 0, len = keyCode.length; i < len; i++){
14801                         if(keyCode[i] == k){
14802                           fn.call(scope || window, dlg, k, e);
14803                           return;
14804                         }
14805                     }
14806                 }else{
14807                     if(k == keyCode){
14808                         fn.call(scope || window, dlg, k, e);
14809                     }
14810                 }
14811             }
14812         };
14813         this.on("keydown", handler);
14814         return this;
14815     },
14816
14817     /**
14818      * Returns the TabPanel component (creates it if it doesn't exist).
14819      * Note: If you wish to simply check for the existence of tabs without creating them,
14820      * check for a null 'tabs' property.
14821      * @return {Roo.TabPanel} The tabs component
14822      */
14823     getTabs : function(){
14824         if(!this.tabs){
14825             this.el.addClass("x-dlg-auto-tabs");
14826             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14827             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14828         }
14829         return this.tabs;
14830     },
14831
14832     /**
14833      * Adds a button to the footer section of the dialog.
14834      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14835      * object or a valid Roo.DomHelper element config
14836      * @param {Function} handler The function called when the button is clicked
14837      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14838      * @return {Roo.Button} The new button
14839      */
14840     addButton : function(config, handler, scope){
14841         var dh = Roo.DomHelper;
14842         if(!this.footer){
14843             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14844         }
14845         if(!this.btnContainer){
14846             var tb = this.footer.createChild({
14847
14848                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14849                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14850             }, null, true);
14851             this.btnContainer = tb.firstChild.firstChild.firstChild;
14852         }
14853         var bconfig = {
14854             handler: handler,
14855             scope: scope,
14856             minWidth: this.minButtonWidth,
14857             hideParent:true
14858         };
14859         if(typeof config == "string"){
14860             bconfig.text = config;
14861         }else{
14862             if(config.tag){
14863                 bconfig.dhconfig = config;
14864             }else{
14865                 Roo.apply(bconfig, config);
14866             }
14867         }
14868         var fc = false;
14869         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14870             bconfig.position = Math.max(0, bconfig.position);
14871             fc = this.btnContainer.childNodes[bconfig.position];
14872         }
14873          
14874         var btn = new Roo.Button(
14875             fc ? 
14876                 this.btnContainer.insertBefore(document.createElement("td"),fc)
14877                 : this.btnContainer.appendChild(document.createElement("td")),
14878             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
14879             bconfig
14880         );
14881         this.syncBodyHeight();
14882         if(!this.buttons){
14883             /**
14884              * Array of all the buttons that have been added to this dialog via addButton
14885              * @type Array
14886              */
14887             this.buttons = [];
14888         }
14889         this.buttons.push(btn);
14890         return btn;
14891     },
14892
14893     /**
14894      * Sets the default button to be focused when the dialog is displayed.
14895      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14896      * @return {Roo.BasicDialog} this
14897      */
14898     setDefaultButton : function(btn){
14899         this.defaultButton = btn;
14900         return this;
14901     },
14902
14903     // private
14904     getHeaderFooterHeight : function(safe){
14905         var height = 0;
14906         if(this.header){
14907            height += this.header.getHeight();
14908         }
14909         if(this.footer){
14910            var fm = this.footer.getMargins();
14911             height += (this.footer.getHeight()+fm.top+fm.bottom);
14912         }
14913         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14914         height += this.centerBg.getPadding("tb");
14915         return height;
14916     },
14917
14918     // private
14919     syncBodyHeight : function()
14920     {
14921         var bd = this.body, // the text
14922             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
14923             bw = this.bwrap;
14924         var height = this.size.height - this.getHeaderFooterHeight(false);
14925         bd.setHeight(height-bd.getMargins("tb"));
14926         var hh = this.header.getHeight();
14927         var h = this.size.height-hh;
14928         cb.setHeight(h);
14929         
14930         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14931         bw.setHeight(h-cb.getPadding("tb"));
14932         
14933         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14934         bd.setWidth(bw.getWidth(true));
14935         if(this.tabs){
14936             this.tabs.syncHeight();
14937             if(Roo.isIE){
14938                 this.tabs.el.repaint();
14939             }
14940         }
14941     },
14942
14943     /**
14944      * Restores the previous state of the dialog if Roo.state is configured.
14945      * @return {Roo.BasicDialog} this
14946      */
14947     restoreState : function(){
14948         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14949         if(box && box.width){
14950             this.xy = [box.x, box.y];
14951             this.resizeTo(box.width, box.height);
14952         }
14953         return this;
14954     },
14955
14956     // private
14957     beforeShow : function(){
14958         this.expand();
14959         if(this.fixedcenter){
14960             this.xy = this.el.getCenterXY(true);
14961         }
14962         if(this.modal){
14963             Roo.get(document.body).addClass("x-body-masked");
14964             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14965             this.mask.show();
14966         }
14967         this.constrainXY();
14968     },
14969
14970     // private
14971     animShow : function(){
14972         var b = Roo.get(this.animateTarget).getBox();
14973         this.proxy.setSize(b.width, b.height);
14974         this.proxy.setLocation(b.x, b.y);
14975         this.proxy.show();
14976         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
14977                     true, .35, this.showEl.createDelegate(this));
14978     },
14979
14980     /**
14981      * Shows the dialog.
14982      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
14983      * @return {Roo.BasicDialog} this
14984      */
14985     show : function(animateTarget){
14986         if (this.fireEvent("beforeshow", this) === false){
14987             return;
14988         }
14989         if(this.syncHeightBeforeShow){
14990             this.syncBodyHeight();
14991         }else if(this.firstShow){
14992             this.firstShow = false;
14993             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
14994         }
14995         this.animateTarget = animateTarget || this.animateTarget;
14996         if(!this.el.isVisible()){
14997             this.beforeShow();
14998             if(this.animateTarget && Roo.get(this.animateTarget)){
14999                 this.animShow();
15000             }else{
15001                 this.showEl();
15002             }
15003         }
15004         return this;
15005     },
15006
15007     // private
15008     showEl : function(){
15009         this.proxy.hide();
15010         this.el.setXY(this.xy);
15011         this.el.show();
15012         this.adjustAssets(true);
15013         this.toFront();
15014         this.focus();
15015         // IE peekaboo bug - fix found by Dave Fenwick
15016         if(Roo.isIE){
15017             this.el.repaint();
15018         }
15019         this.fireEvent("show", this);
15020     },
15021
15022     /**
15023      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
15024      * dialog itself will receive focus.
15025      */
15026     focus : function(){
15027         if(this.defaultButton){
15028             this.defaultButton.focus();
15029         }else{
15030             this.focusEl.focus();
15031         }
15032     },
15033
15034     // private
15035     constrainXY : function(){
15036         if(this.constraintoviewport !== false){
15037             if(!this.viewSize){
15038                 if(this.container){
15039                     var s = this.container.getSize();
15040                     this.viewSize = [s.width, s.height];
15041                 }else{
15042                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
15043                 }
15044             }
15045             var s = Roo.get(this.container||document).getScroll();
15046
15047             var x = this.xy[0], y = this.xy[1];
15048             var w = this.size.width, h = this.size.height;
15049             var vw = this.viewSize[0], vh = this.viewSize[1];
15050             // only move it if it needs it
15051             var moved = false;
15052             // first validate right/bottom
15053             if(x + w > vw+s.left){
15054                 x = vw - w;
15055                 moved = true;
15056             }
15057             if(y + h > vh+s.top){
15058                 y = vh - h;
15059                 moved = true;
15060             }
15061             // then make sure top/left isn't negative
15062             if(x < s.left){
15063                 x = s.left;
15064                 moved = true;
15065             }
15066             if(y < s.top){
15067                 y = s.top;
15068                 moved = true;
15069             }
15070             if(moved){
15071                 // cache xy
15072                 this.xy = [x, y];
15073                 if(this.isVisible()){
15074                     this.el.setLocation(x, y);
15075                     this.adjustAssets();
15076                 }
15077             }
15078         }
15079     },
15080
15081     // private
15082     onDrag : function(){
15083         if(!this.proxyDrag){
15084             this.xy = this.el.getXY();
15085             this.adjustAssets();
15086         }
15087     },
15088
15089     // private
15090     adjustAssets : function(doShow){
15091         var x = this.xy[0], y = this.xy[1];
15092         var w = this.size.width, h = this.size.height;
15093         if(doShow === true){
15094             if(this.shadow){
15095                 this.shadow.show(this.el);
15096             }
15097             if(this.shim){
15098                 this.shim.show();
15099             }
15100         }
15101         if(this.shadow && this.shadow.isVisible()){
15102             this.shadow.show(this.el);
15103         }
15104         if(this.shim && this.shim.isVisible()){
15105             this.shim.setBounds(x, y, w, h);
15106         }
15107     },
15108
15109     // private
15110     adjustViewport : function(w, h){
15111         if(!w || !h){
15112             w = Roo.lib.Dom.getViewWidth();
15113             h = Roo.lib.Dom.getViewHeight();
15114         }
15115         // cache the size
15116         this.viewSize = [w, h];
15117         if(this.modal && this.mask.isVisible()){
15118             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
15119             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15120         }
15121         if(this.isVisible()){
15122             this.constrainXY();
15123         }
15124     },
15125
15126     /**
15127      * Destroys this dialog and all its supporting elements (including any tabs, shim,
15128      * shadow, proxy, mask, etc.)  Also removes all event listeners.
15129      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
15130      */
15131     destroy : function(removeEl){
15132         if(this.isVisible()){
15133             this.animateTarget = null;
15134             this.hide();
15135         }
15136         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
15137         if(this.tabs){
15138             this.tabs.destroy(removeEl);
15139         }
15140         Roo.destroy(
15141              this.shim,
15142              this.proxy,
15143              this.resizer,
15144              this.close,
15145              this.mask
15146         );
15147         if(this.dd){
15148             this.dd.unreg();
15149         }
15150         if(this.buttons){
15151            for(var i = 0, len = this.buttons.length; i < len; i++){
15152                this.buttons[i].destroy();
15153            }
15154         }
15155         this.el.removeAllListeners();
15156         if(removeEl === true){
15157             this.el.update("");
15158             this.el.remove();
15159         }
15160         Roo.DialogManager.unregister(this);
15161     },
15162
15163     // private
15164     startMove : function(){
15165         if(this.proxyDrag){
15166             this.proxy.show();
15167         }
15168         if(this.constraintoviewport !== false){
15169             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
15170         }
15171     },
15172
15173     // private
15174     endMove : function(){
15175         if(!this.proxyDrag){
15176             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
15177         }else{
15178             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
15179             this.proxy.hide();
15180         }
15181         this.refreshSize();
15182         this.adjustAssets();
15183         this.focus();
15184         this.fireEvent("move", this, this.xy[0], this.xy[1]);
15185     },
15186
15187     /**
15188      * Brings this dialog to the front of any other visible dialogs
15189      * @return {Roo.BasicDialog} this
15190      */
15191     toFront : function(){
15192         Roo.DialogManager.bringToFront(this);
15193         return this;
15194     },
15195
15196     /**
15197      * Sends this dialog to the back (under) of any other visible dialogs
15198      * @return {Roo.BasicDialog} this
15199      */
15200     toBack : function(){
15201         Roo.DialogManager.sendToBack(this);
15202         return this;
15203     },
15204
15205     /**
15206      * Centers this dialog in the viewport
15207      * @return {Roo.BasicDialog} this
15208      */
15209     center : function(){
15210         var xy = this.el.getCenterXY(true);
15211         this.moveTo(xy[0], xy[1]);
15212         return this;
15213     },
15214
15215     /**
15216      * Moves the dialog's top-left corner to the specified point
15217      * @param {Number} x
15218      * @param {Number} y
15219      * @return {Roo.BasicDialog} this
15220      */
15221     moveTo : function(x, y){
15222         this.xy = [x,y];
15223         if(this.isVisible()){
15224             this.el.setXY(this.xy);
15225             this.adjustAssets();
15226         }
15227         return this;
15228     },
15229
15230     /**
15231      * Aligns the dialog to the specified element
15232      * @param {String/HTMLElement/Roo.Element} element The element to align to.
15233      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
15234      * @param {Array} offsets (optional) Offset the positioning by [x, y]
15235      * @return {Roo.BasicDialog} this
15236      */
15237     alignTo : function(element, position, offsets){
15238         this.xy = this.el.getAlignToXY(element, position, offsets);
15239         if(this.isVisible()){
15240             this.el.setXY(this.xy);
15241             this.adjustAssets();
15242         }
15243         return this;
15244     },
15245
15246     /**
15247      * Anchors an element to another element and realigns it when the window is resized.
15248      * @param {String/HTMLElement/Roo.Element} element The element to align to.
15249      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
15250      * @param {Array} offsets (optional) Offset the positioning by [x, y]
15251      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
15252      * is a number, it is used as the buffer delay (defaults to 50ms).
15253      * @return {Roo.BasicDialog} this
15254      */
15255     anchorTo : function(el, alignment, offsets, monitorScroll){
15256         var action = function(){
15257             this.alignTo(el, alignment, offsets);
15258         };
15259         Roo.EventManager.onWindowResize(action, this);
15260         var tm = typeof monitorScroll;
15261         if(tm != 'undefined'){
15262             Roo.EventManager.on(window, 'scroll', action, this,
15263                 {buffer: tm == 'number' ? monitorScroll : 50});
15264         }
15265         action.call(this);
15266         return this;
15267     },
15268
15269     /**
15270      * Returns true if the dialog is visible
15271      * @return {Boolean}
15272      */
15273     isVisible : function(){
15274         return this.el.isVisible();
15275     },
15276
15277     // private
15278     animHide : function(callback){
15279         var b = Roo.get(this.animateTarget).getBox();
15280         this.proxy.show();
15281         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
15282         this.el.hide();
15283         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
15284                     this.hideEl.createDelegate(this, [callback]));
15285     },
15286
15287     /**
15288      * Hides the dialog.
15289      * @param {Function} callback (optional) Function to call when the dialog is hidden
15290      * @return {Roo.BasicDialog} this
15291      */
15292     hide : function(callback){
15293         if (this.fireEvent("beforehide", this) === false){
15294             return;
15295         }
15296         if(this.shadow){
15297             this.shadow.hide();
15298         }
15299         if(this.shim) {
15300           this.shim.hide();
15301         }
15302         // sometimes animateTarget seems to get set.. causing problems...
15303         // this just double checks..
15304         if(this.animateTarget && Roo.get(this.animateTarget)) {
15305            this.animHide(callback);
15306         }else{
15307             this.el.hide();
15308             this.hideEl(callback);
15309         }
15310         return this;
15311     },
15312
15313     // private
15314     hideEl : function(callback){
15315         this.proxy.hide();
15316         if(this.modal){
15317             this.mask.hide();
15318             Roo.get(document.body).removeClass("x-body-masked");
15319         }
15320         this.fireEvent("hide", this);
15321         if(typeof callback == "function"){
15322             callback();
15323         }
15324     },
15325
15326     // private
15327     hideAction : function(){
15328         this.setLeft("-10000px");
15329         this.setTop("-10000px");
15330         this.setStyle("visibility", "hidden");
15331     },
15332
15333     // private
15334     refreshSize : function(){
15335         this.size = this.el.getSize();
15336         this.xy = this.el.getXY();
15337         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
15338     },
15339
15340     // private
15341     // z-index is managed by the DialogManager and may be overwritten at any time
15342     setZIndex : function(index){
15343         if(this.modal){
15344             this.mask.setStyle("z-index", index);
15345         }
15346         if(this.shim){
15347             this.shim.setStyle("z-index", ++index);
15348         }
15349         if(this.shadow){
15350             this.shadow.setZIndex(++index);
15351         }
15352         this.el.setStyle("z-index", ++index);
15353         if(this.proxy){
15354             this.proxy.setStyle("z-index", ++index);
15355         }
15356         if(this.resizer){
15357             this.resizer.proxy.setStyle("z-index", ++index);
15358         }
15359
15360         this.lastZIndex = index;
15361     },
15362
15363     /**
15364      * Returns the element for this dialog
15365      * @return {Roo.Element} The underlying dialog Element
15366      */
15367     getEl : function(){
15368         return this.el;
15369     }
15370 });
15371
15372 /**
15373  * @class Roo.DialogManager
15374  * Provides global access to BasicDialogs that have been created and
15375  * support for z-indexing (layering) multiple open dialogs.
15376  */
15377 Roo.DialogManager = function(){
15378     var list = {};
15379     var accessList = [];
15380     var front = null;
15381
15382     // private
15383     var sortDialogs = function(d1, d2){
15384         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
15385     };
15386
15387     // private
15388     var orderDialogs = function(){
15389         accessList.sort(sortDialogs);
15390         var seed = Roo.DialogManager.zseed;
15391         for(var i = 0, len = accessList.length; i < len; i++){
15392             var dlg = accessList[i];
15393             if(dlg){
15394                 dlg.setZIndex(seed + (i*10));
15395             }
15396         }
15397     };
15398
15399     return {
15400         /**
15401          * The starting z-index for BasicDialogs (defaults to 9000)
15402          * @type Number The z-index value
15403          */
15404         zseed : 9000,
15405
15406         // private
15407         register : function(dlg){
15408             list[dlg.id] = dlg;
15409             accessList.push(dlg);
15410         },
15411
15412         // private
15413         unregister : function(dlg){
15414             delete list[dlg.id];
15415             var i=0;
15416             var len=0;
15417             if(!accessList.indexOf){
15418                 for(  i = 0, len = accessList.length; i < len; i++){
15419                     if(accessList[i] == dlg){
15420                         accessList.splice(i, 1);
15421                         return;
15422                     }
15423                 }
15424             }else{
15425                  i = accessList.indexOf(dlg);
15426                 if(i != -1){
15427                     accessList.splice(i, 1);
15428                 }
15429             }
15430         },
15431
15432         /**
15433          * Gets a registered dialog by id
15434          * @param {String/Object} id The id of the dialog or a dialog
15435          * @return {Roo.BasicDialog} this
15436          */
15437         get : function(id){
15438             return typeof id == "object" ? id : list[id];
15439         },
15440
15441         /**
15442          * Brings the specified dialog to the front
15443          * @param {String/Object} dlg The id of the dialog or a dialog
15444          * @return {Roo.BasicDialog} this
15445          */
15446         bringToFront : function(dlg){
15447             dlg = this.get(dlg);
15448             if(dlg != front){
15449                 front = dlg;
15450                 dlg._lastAccess = new Date().getTime();
15451                 orderDialogs();
15452             }
15453             return dlg;
15454         },
15455
15456         /**
15457          * Sends the specified dialog to the back
15458          * @param {String/Object} dlg The id of the dialog or a dialog
15459          * @return {Roo.BasicDialog} this
15460          */
15461         sendToBack : function(dlg){
15462             dlg = this.get(dlg);
15463             dlg._lastAccess = -(new Date().getTime());
15464             orderDialogs();
15465             return dlg;
15466         },
15467
15468         /**
15469          * Hides all dialogs
15470          */
15471         hideAll : function(){
15472             for(var id in list){
15473                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
15474                     list[id].hide();
15475                 }
15476             }
15477         }
15478     };
15479 }();
15480
15481 /**
15482  * @class Roo.LayoutDialog
15483  * @extends Roo.BasicDialog
15484  * Dialog which provides adjustments for working with a layout in a Dialog.
15485  * Add your necessary layout config options to the dialog's config.<br>
15486  * Example usage (including a nested layout):
15487  * <pre><code>
15488 if(!dialog){
15489     dialog = new Roo.LayoutDialog("download-dlg", {
15490         modal: true,
15491         width:600,
15492         height:450,
15493         shadow:true,
15494         minWidth:500,
15495         minHeight:350,
15496         autoTabs:true,
15497         proxyDrag:true,
15498         // layout config merges with the dialog config
15499         center:{
15500             tabPosition: "top",
15501             alwaysShowTabs: true
15502         }
15503     });
15504     dialog.addKeyListener(27, dialog.hide, dialog);
15505     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
15506     dialog.addButton("Build It!", this.getDownload, this);
15507
15508     // we can even add nested layouts
15509     var innerLayout = new Roo.BorderLayout("dl-inner", {
15510         east: {
15511             initialSize: 200,
15512             autoScroll:true,
15513             split:true
15514         },
15515         center: {
15516             autoScroll:true
15517         }
15518     });
15519     innerLayout.beginUpdate();
15520     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
15521     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
15522     innerLayout.endUpdate(true);
15523
15524     var layout = dialog.getLayout();
15525     layout.beginUpdate();
15526     layout.add("center", new Roo.ContentPanel("standard-panel",
15527                         {title: "Download the Source", fitToFrame:true}));
15528     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
15529                {title: "Build your own roo.js"}));
15530     layout.getRegion("center").showPanel(sp);
15531     layout.endUpdate();
15532 }
15533 </code></pre>
15534     * @constructor
15535     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
15536     * @param {Object} config configuration options
15537   */
15538 Roo.LayoutDialog = function(el, cfg){
15539     
15540     var config=  cfg;
15541     if (typeof(cfg) == 'undefined') {
15542         config = Roo.apply({}, el);
15543         // not sure why we use documentElement here.. - it should always be body.
15544         // IE7 borks horribly if we use documentElement.
15545         // webkit also does not like documentElement - it creates a body element...
15546         el = Roo.get( document.body || document.documentElement ).createChild();
15547         //config.autoCreate = true;
15548     }
15549     
15550     
15551     config.autoTabs = false;
15552     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
15553     this.body.setStyle({overflow:"hidden", position:"relative"});
15554     this.layout = new Roo.BorderLayout(this.body.dom, config);
15555     this.layout.monitorWindowResize = false;
15556     this.el.addClass("x-dlg-auto-layout");
15557     // fix case when center region overwrites center function
15558     this.center = Roo.BasicDialog.prototype.center;
15559     this.on("show", this.layout.layout, this.layout, true);
15560     if (config.items) {
15561         var xitems = config.items;
15562         delete config.items;
15563         Roo.each(xitems, this.addxtype, this);
15564     }
15565     
15566     
15567 };
15568 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
15569     /**
15570      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
15571      * @deprecated
15572      */
15573     endUpdate : function(){
15574         this.layout.endUpdate();
15575     },
15576
15577     /**
15578      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
15579      *  @deprecated
15580      */
15581     beginUpdate : function(){
15582         this.layout.beginUpdate();
15583     },
15584
15585     /**
15586      * Get the BorderLayout for this dialog
15587      * @return {Roo.BorderLayout}
15588      */
15589     getLayout : function(){
15590         return this.layout;
15591     },
15592
15593     showEl : function(){
15594         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
15595         if(Roo.isIE7){
15596             this.layout.layout();
15597         }
15598     },
15599
15600     // private
15601     // Use the syncHeightBeforeShow config option to control this automatically
15602     syncBodyHeight : function(){
15603         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
15604         if(this.layout){this.layout.layout();}
15605     },
15606     
15607       /**
15608      * Add an xtype element (actually adds to the layout.)
15609      * @return {Object} xdata xtype object data.
15610      */
15611     
15612     addxtype : function(c) {
15613         return this.layout.addxtype(c);
15614     }
15615 });/*
15616  * Based on:
15617  * Ext JS Library 1.1.1
15618  * Copyright(c) 2006-2007, Ext JS, LLC.
15619  *
15620  * Originally Released Under LGPL - original licence link has changed is not relivant.
15621  *
15622  * Fork - LGPL
15623  * <script type="text/javascript">
15624  */
15625  
15626 /**
15627  * @class Roo.MessageBox
15628  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
15629  * Example usage:
15630  *<pre><code>
15631 // Basic alert:
15632 Roo.Msg.alert('Status', 'Changes saved successfully.');
15633
15634 // Prompt for user data:
15635 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
15636     if (btn == 'ok'){
15637         // process text value...
15638     }
15639 });
15640
15641 // Show a dialog using config options:
15642 Roo.Msg.show({
15643    title:'Save Changes?',
15644    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
15645    buttons: Roo.Msg.YESNOCANCEL,
15646    fn: processResult,
15647    animEl: 'elId'
15648 });
15649 </code></pre>
15650  * @singleton
15651  */
15652 Roo.MessageBox = function(){
15653     var dlg, opt, mask, waitTimer;
15654     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
15655     var buttons, activeTextEl, bwidth;
15656
15657     // private
15658     var handleButton = function(button){
15659         dlg.hide();
15660         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
15661     };
15662
15663     // private
15664     var handleHide = function(){
15665         if(opt && opt.cls){
15666             dlg.el.removeClass(opt.cls);
15667         }
15668         if(waitTimer){
15669             Roo.TaskMgr.stop(waitTimer);
15670             waitTimer = null;
15671         }
15672     };
15673
15674     // private
15675     var updateButtons = function(b){
15676         var width = 0;
15677         if(!b){
15678             buttons["ok"].hide();
15679             buttons["cancel"].hide();
15680             buttons["yes"].hide();
15681             buttons["no"].hide();
15682             dlg.footer.dom.style.display = 'none';
15683             return width;
15684         }
15685         dlg.footer.dom.style.display = '';
15686         for(var k in buttons){
15687             if(typeof buttons[k] != "function"){
15688                 if(b[k]){
15689                     buttons[k].show();
15690                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15691                     width += buttons[k].el.getWidth()+15;
15692                 }else{
15693                     buttons[k].hide();
15694                 }
15695             }
15696         }
15697         return width;
15698     };
15699
15700     // private
15701     var handleEsc = function(d, k, e){
15702         if(opt && opt.closable !== false){
15703             dlg.hide();
15704         }
15705         if(e){
15706             e.stopEvent();
15707         }
15708     };
15709
15710     return {
15711         /**
15712          * Returns a reference to the underlying {@link Roo.BasicDialog} element
15713          * @return {Roo.BasicDialog} The BasicDialog element
15714          */
15715         getDialog : function(){
15716            if(!dlg){
15717                 dlg = new Roo.BasicDialog("x-msg-box", {
15718                     autoCreate : true,
15719                     shadow: true,
15720                     draggable: true,
15721                     resizable:false,
15722                     constraintoviewport:false,
15723                     fixedcenter:true,
15724                     collapsible : false,
15725                     shim:true,
15726                     modal: true,
15727                     width:400, height:100,
15728                     buttonAlign:"center",
15729                     closeClick : function(){
15730                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15731                             handleButton("no");
15732                         }else{
15733                             handleButton("cancel");
15734                         }
15735                     }
15736                 });
15737                 dlg.on("hide", handleHide);
15738                 mask = dlg.mask;
15739                 dlg.addKeyListener(27, handleEsc);
15740                 buttons = {};
15741                 var bt = this.buttonText;
15742                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15743                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15744                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15745                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15746                 bodyEl = dlg.body.createChild({
15747
15748                     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>'
15749                 });
15750                 msgEl = bodyEl.dom.firstChild;
15751                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15752                 textboxEl.enableDisplayMode();
15753                 textboxEl.addKeyListener([10,13], function(){
15754                     if(dlg.isVisible() && opt && opt.buttons){
15755                         if(opt.buttons.ok){
15756                             handleButton("ok");
15757                         }else if(opt.buttons.yes){
15758                             handleButton("yes");
15759                         }
15760                     }
15761                 });
15762                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15763                 textareaEl.enableDisplayMode();
15764                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15765                 progressEl.enableDisplayMode();
15766                 var pf = progressEl.dom.firstChild;
15767                 if (pf) {
15768                     pp = Roo.get(pf.firstChild);
15769                     pp.setHeight(pf.offsetHeight);
15770                 }
15771                 
15772             }
15773             return dlg;
15774         },
15775
15776         /**
15777          * Updates the message box body text
15778          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15779          * the XHTML-compliant non-breaking space character '&amp;#160;')
15780          * @return {Roo.MessageBox} This message box
15781          */
15782         updateText : function(text){
15783             if(!dlg.isVisible() && !opt.width){
15784                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15785             }
15786             msgEl.innerHTML = text || '&#160;';
15787       
15788             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
15789             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
15790             var w = Math.max(
15791                     Math.min(opt.width || cw , this.maxWidth), 
15792                     Math.max(opt.minWidth || this.minWidth, bwidth)
15793             );
15794             if(opt.prompt){
15795                 activeTextEl.setWidth(w);
15796             }
15797             if(dlg.isVisible()){
15798                 dlg.fixedcenter = false;
15799             }
15800             // to big, make it scroll. = But as usual stupid IE does not support
15801             // !important..
15802             
15803             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
15804                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
15805                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
15806             } else {
15807                 bodyEl.dom.style.height = '';
15808                 bodyEl.dom.style.overflowY = '';
15809             }
15810             if (cw > w) {
15811                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
15812             } else {
15813                 bodyEl.dom.style.overflowX = '';
15814             }
15815             
15816             dlg.setContentSize(w, bodyEl.getHeight());
15817             if(dlg.isVisible()){
15818                 dlg.fixedcenter = true;
15819             }
15820             return this;
15821         },
15822
15823         /**
15824          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
15825          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15826          * @param {Number} value Any number between 0 and 1 (e.g., .5)
15827          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15828          * @return {Roo.MessageBox} This message box
15829          */
15830         updateProgress : function(value, text){
15831             if(text){
15832                 this.updateText(text);
15833             }
15834             if (pp) { // weird bug on my firefox - for some reason this is not defined
15835                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15836             }
15837             return this;
15838         },        
15839
15840         /**
15841          * Returns true if the message box is currently displayed
15842          * @return {Boolean} True if the message box is visible, else false
15843          */
15844         isVisible : function(){
15845             return dlg && dlg.isVisible();  
15846         },
15847
15848         /**
15849          * Hides the message box if it is displayed
15850          */
15851         hide : function(){
15852             if(this.isVisible()){
15853                 dlg.hide();
15854             }  
15855         },
15856
15857         /**
15858          * Displays a new message box, or reinitializes an existing message box, based on the config options
15859          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15860          * The following config object properties are supported:
15861          * <pre>
15862 Property    Type             Description
15863 ----------  ---------------  ------------------------------------------------------------------------------------
15864 animEl            String/Element   An id or Element from which the message box should animate as it opens and
15865                                    closes (defaults to undefined)
15866 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15867                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
15868 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
15869                                    progress and wait dialogs will ignore this property and always hide the
15870                                    close button as they can only be closed programmatically.
15871 cls               String           A custom CSS class to apply to the message box element
15872 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
15873                                    displayed (defaults to 75)
15874 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
15875                                    function will be btn (the name of the button that was clicked, if applicable,
15876                                    e.g. "ok"), and text (the value of the active text field, if applicable).
15877                                    Progress and wait dialogs will ignore this option since they do not respond to
15878                                    user actions and can only be closed programmatically, so any required function
15879                                    should be called by the same code after it closes the dialog.
15880 icon              String           A CSS class that provides a background image to be used as an icon for
15881                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15882 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
15883 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
15884 modal             Boolean          False to allow user interaction with the page while the message box is
15885                                    displayed (defaults to true)
15886 msg               String           A string that will replace the existing message box body text (defaults
15887                                    to the XHTML-compliant non-breaking space character '&#160;')
15888 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
15889 progress          Boolean          True to display a progress bar (defaults to false)
15890 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
15891 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
15892 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
15893 title             String           The title text
15894 value             String           The string value to set into the active textbox element if displayed
15895 wait              Boolean          True to display a progress bar (defaults to false)
15896 width             Number           The width of the dialog in pixels
15897 </pre>
15898          *
15899          * Example usage:
15900          * <pre><code>
15901 Roo.Msg.show({
15902    title: 'Address',
15903    msg: 'Please enter your address:',
15904    width: 300,
15905    buttons: Roo.MessageBox.OKCANCEL,
15906    multiline: true,
15907    fn: saveAddress,
15908    animEl: 'addAddressBtn'
15909 });
15910 </code></pre>
15911          * @param {Object} config Configuration options
15912          * @return {Roo.MessageBox} This message box
15913          */
15914         show : function(options)
15915         {
15916             
15917             // this causes nightmares if you show one dialog after another
15918             // especially on callbacks..
15919              
15920             if(this.isVisible()){
15921                 
15922                 this.hide();
15923                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
15924                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
15925                 Roo.log("New Dialog Message:" +  options.msg )
15926                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
15927                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
15928                 
15929             }
15930             var d = this.getDialog();
15931             opt = options;
15932             d.setTitle(opt.title || "&#160;");
15933             d.close.setDisplayed(opt.closable !== false);
15934             activeTextEl = textboxEl;
15935             opt.prompt = opt.prompt || (opt.multiline ? true : false);
15936             if(opt.prompt){
15937                 if(opt.multiline){
15938                     textboxEl.hide();
15939                     textareaEl.show();
15940                     textareaEl.setHeight(typeof opt.multiline == "number" ?
15941                         opt.multiline : this.defaultTextHeight);
15942                     activeTextEl = textareaEl;
15943                 }else{
15944                     textboxEl.show();
15945                     textareaEl.hide();
15946                 }
15947             }else{
15948                 textboxEl.hide();
15949                 textareaEl.hide();
15950             }
15951             progressEl.setDisplayed(opt.progress === true);
15952             this.updateProgress(0);
15953             activeTextEl.dom.value = opt.value || "";
15954             if(opt.prompt){
15955                 dlg.setDefaultButton(activeTextEl);
15956             }else{
15957                 var bs = opt.buttons;
15958                 var db = null;
15959                 if(bs && bs.ok){
15960                     db = buttons["ok"];
15961                 }else if(bs && bs.yes){
15962                     db = buttons["yes"];
15963                 }
15964                 dlg.setDefaultButton(db);
15965             }
15966             bwidth = updateButtons(opt.buttons);
15967             this.updateText(opt.msg);
15968             if(opt.cls){
15969                 d.el.addClass(opt.cls);
15970             }
15971             d.proxyDrag = opt.proxyDrag === true;
15972             d.modal = opt.modal !== false;
15973             d.mask = opt.modal !== false ? mask : false;
15974             if(!d.isVisible()){
15975                 // force it to the end of the z-index stack so it gets a cursor in FF
15976                 document.body.appendChild(dlg.el.dom);
15977                 d.animateTarget = null;
15978                 d.show(options.animEl);
15979             }
15980             return this;
15981         },
15982
15983         /**
15984          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
15985          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
15986          * and closing the message box when the process is complete.
15987          * @param {String} title The title bar text
15988          * @param {String} msg The message box body text
15989          * @return {Roo.MessageBox} This message box
15990          */
15991         progress : function(title, msg){
15992             this.show({
15993                 title : title,
15994                 msg : msg,
15995                 buttons: false,
15996                 progress:true,
15997                 closable:false,
15998                 minWidth: this.minProgressWidth,
15999                 modal : true
16000             });
16001             return this;
16002         },
16003
16004         /**
16005          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
16006          * If a callback function is passed it will be called after the user clicks the button, and the
16007          * id of the button that was clicked will be passed as the only parameter to the callback
16008          * (could also be the top-right close button).
16009          * @param {String} title The title bar text
16010          * @param {String} msg The message box body text
16011          * @param {Function} fn (optional) The callback function invoked after the message box is closed
16012          * @param {Object} scope (optional) The scope of the callback function
16013          * @return {Roo.MessageBox} This message box
16014          */
16015         alert : function(title, msg, fn, scope){
16016             this.show({
16017                 title : title,
16018                 msg : msg,
16019                 buttons: this.OK,
16020                 fn: fn,
16021                 scope : scope,
16022                 modal : true
16023             });
16024             return this;
16025         },
16026
16027         /**
16028          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
16029          * interaction while waiting for a long-running process to complete that does not have defined intervals.
16030          * You are responsible for closing the message box when the process is complete.
16031          * @param {String} msg The message box body text
16032          * @param {String} title (optional) The title bar text
16033          * @return {Roo.MessageBox} This message box
16034          */
16035         wait : function(msg, title){
16036             this.show({
16037                 title : title,
16038                 msg : msg,
16039                 buttons: false,
16040                 closable:false,
16041                 progress:true,
16042                 modal:true,
16043                 width:300,
16044                 wait:true
16045             });
16046             waitTimer = Roo.TaskMgr.start({
16047                 run: function(i){
16048                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
16049                 },
16050                 interval: 1000
16051             });
16052             return this;
16053         },
16054
16055         /**
16056          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
16057          * If a callback function is passed it will be called after the user clicks either button, and the id of the
16058          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
16059          * @param {String} title The title bar text
16060          * @param {String} msg The message box body text
16061          * @param {Function} fn (optional) The callback function invoked after the message box is closed
16062          * @param {Object} scope (optional) The scope of the callback function
16063          * @return {Roo.MessageBox} This message box
16064          */
16065         confirm : function(title, msg, fn, scope){
16066             this.show({
16067                 title : title,
16068                 msg : msg,
16069                 buttons: this.YESNO,
16070                 fn: fn,
16071                 scope : scope,
16072                 modal : true
16073             });
16074             return this;
16075         },
16076
16077         /**
16078          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
16079          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
16080          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
16081          * (could also be the top-right close button) and the text that was entered will be passed as the two
16082          * parameters to the callback.
16083          * @param {String} title The title bar text
16084          * @param {String} msg The message box body text
16085          * @param {Function} fn (optional) The callback function invoked after the message box is closed
16086          * @param {Object} scope (optional) The scope of the callback function
16087          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
16088          * property, or the height in pixels to create the textbox (defaults to false / single-line)
16089          * @return {Roo.MessageBox} This message box
16090          */
16091         prompt : function(title, msg, fn, scope, multiline){
16092             this.show({
16093                 title : title,
16094                 msg : msg,
16095                 buttons: this.OKCANCEL,
16096                 fn: fn,
16097                 minWidth:250,
16098                 scope : scope,
16099                 prompt:true,
16100                 multiline: multiline,
16101                 modal : true
16102             });
16103             return this;
16104         },
16105
16106         /**
16107          * Button config that displays a single OK button
16108          * @type Object
16109          */
16110         OK : {ok:true},
16111         /**
16112          * Button config that displays Yes and No buttons
16113          * @type Object
16114          */
16115         YESNO : {yes:true, no:true},
16116         /**
16117          * Button config that displays OK and Cancel buttons
16118          * @type Object
16119          */
16120         OKCANCEL : {ok:true, cancel:true},
16121         /**
16122          * Button config that displays Yes, No and Cancel buttons
16123          * @type Object
16124          */
16125         YESNOCANCEL : {yes:true, no:true, cancel:true},
16126
16127         /**
16128          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
16129          * @type Number
16130          */
16131         defaultTextHeight : 75,
16132         /**
16133          * The maximum width in pixels of the message box (defaults to 600)
16134          * @type Number
16135          */
16136         maxWidth : 600,
16137         /**
16138          * The minimum width in pixels of the message box (defaults to 100)
16139          * @type Number
16140          */
16141         minWidth : 100,
16142         /**
16143          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
16144          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
16145          * @type Number
16146          */
16147         minProgressWidth : 250,
16148         /**
16149          * An object containing the default button text strings that can be overriden for localized language support.
16150          * Supported properties are: ok, cancel, yes and no.
16151          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
16152          * @type Object
16153          */
16154         buttonText : {
16155             ok : "OK",
16156             cancel : "Cancel",
16157             yes : "Yes",
16158             no : "No"
16159         }
16160     };
16161 }();
16162
16163 /**
16164  * Shorthand for {@link Roo.MessageBox}
16165  */
16166 Roo.Msg = Roo.MessageBox;/*
16167  * Based on:
16168  * Ext JS Library 1.1.1
16169  * Copyright(c) 2006-2007, Ext JS, LLC.
16170  *
16171  * Originally Released Under LGPL - original licence link has changed is not relivant.
16172  *
16173  * Fork - LGPL
16174  * <script type="text/javascript">
16175  */
16176 /**
16177  * @class Roo.QuickTips
16178  * Provides attractive and customizable tooltips for any element.
16179  * @singleton
16180  */
16181 Roo.QuickTips = function(){
16182     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
16183     var ce, bd, xy, dd;
16184     var visible = false, disabled = true, inited = false;
16185     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
16186     
16187     var onOver = function(e){
16188         if(disabled){
16189             return;
16190         }
16191         var t = e.getTarget();
16192         if(!t || t.nodeType !== 1 || t == document || t == document.body){
16193             return;
16194         }
16195         if(ce && t == ce.el){
16196             clearTimeout(hideProc);
16197             return;
16198         }
16199         if(t && tagEls[t.id]){
16200             tagEls[t.id].el = t;
16201             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
16202             return;
16203         }
16204         var ttp, et = Roo.fly(t);
16205         var ns = cfg.namespace;
16206         if(tm.interceptTitles && t.title){
16207             ttp = t.title;
16208             t.qtip = ttp;
16209             t.removeAttribute("title");
16210             e.preventDefault();
16211         }else{
16212             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
16213         }
16214         if(ttp){
16215             showProc = show.defer(tm.showDelay, tm, [{
16216                 el: t, 
16217                 text: ttp, 
16218                 width: et.getAttributeNS(ns, cfg.width),
16219                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
16220                 title: et.getAttributeNS(ns, cfg.title),
16221                     cls: et.getAttributeNS(ns, cfg.cls)
16222             }]);
16223         }
16224     };
16225     
16226     var onOut = function(e){
16227         clearTimeout(showProc);
16228         var t = e.getTarget();
16229         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
16230             hideProc = setTimeout(hide, tm.hideDelay);
16231         }
16232     };
16233     
16234     var onMove = function(e){
16235         if(disabled){
16236             return;
16237         }
16238         xy = e.getXY();
16239         xy[1] += 18;
16240         if(tm.trackMouse && ce){
16241             el.setXY(xy);
16242         }
16243     };
16244     
16245     var onDown = function(e){
16246         clearTimeout(showProc);
16247         clearTimeout(hideProc);
16248         if(!e.within(el)){
16249             if(tm.hideOnClick){
16250                 hide();
16251                 tm.disable();
16252                 tm.enable.defer(100, tm);
16253             }
16254         }
16255     };
16256     
16257     var getPad = function(){
16258         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
16259     };
16260
16261     var show = function(o){
16262         if(disabled){
16263             return;
16264         }
16265         clearTimeout(dismissProc);
16266         ce = o;
16267         if(removeCls){ // in case manually hidden
16268             el.removeClass(removeCls);
16269             removeCls = null;
16270         }
16271         if(ce.cls){
16272             el.addClass(ce.cls);
16273             removeCls = ce.cls;
16274         }
16275         if(ce.title){
16276             tipTitle.update(ce.title);
16277             tipTitle.show();
16278         }else{
16279             tipTitle.update('');
16280             tipTitle.hide();
16281         }
16282         el.dom.style.width  = tm.maxWidth+'px';
16283         //tipBody.dom.style.width = '';
16284         tipBodyText.update(o.text);
16285         var p = getPad(), w = ce.width;
16286         if(!w){
16287             var td = tipBodyText.dom;
16288             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
16289             if(aw > tm.maxWidth){
16290                 w = tm.maxWidth;
16291             }else if(aw < tm.minWidth){
16292                 w = tm.minWidth;
16293             }else{
16294                 w = aw;
16295             }
16296         }
16297         //tipBody.setWidth(w);
16298         el.setWidth(parseInt(w, 10) + p);
16299         if(ce.autoHide === false){
16300             close.setDisplayed(true);
16301             if(dd){
16302                 dd.unlock();
16303             }
16304         }else{
16305             close.setDisplayed(false);
16306             if(dd){
16307                 dd.lock();
16308             }
16309         }
16310         if(xy){
16311             el.avoidY = xy[1]-18;
16312             el.setXY(xy);
16313         }
16314         if(tm.animate){
16315             el.setOpacity(.1);
16316             el.setStyle("visibility", "visible");
16317             el.fadeIn({callback: afterShow});
16318         }else{
16319             afterShow();
16320         }
16321     };
16322     
16323     var afterShow = function(){
16324         if(ce){
16325             el.show();
16326             esc.enable();
16327             if(tm.autoDismiss && ce.autoHide !== false){
16328                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
16329             }
16330         }
16331     };
16332     
16333     var hide = function(noanim){
16334         clearTimeout(dismissProc);
16335         clearTimeout(hideProc);
16336         ce = null;
16337         if(el.isVisible()){
16338             esc.disable();
16339             if(noanim !== true && tm.animate){
16340                 el.fadeOut({callback: afterHide});
16341             }else{
16342                 afterHide();
16343             } 
16344         }
16345     };
16346     
16347     var afterHide = function(){
16348         el.hide();
16349         if(removeCls){
16350             el.removeClass(removeCls);
16351             removeCls = null;
16352         }
16353     };
16354     
16355     return {
16356         /**
16357         * @cfg {Number} minWidth
16358         * The minimum width of the quick tip (defaults to 40)
16359         */
16360        minWidth : 40,
16361         /**
16362         * @cfg {Number} maxWidth
16363         * The maximum width of the quick tip (defaults to 300)
16364         */
16365        maxWidth : 300,
16366         /**
16367         * @cfg {Boolean} interceptTitles
16368         * True to automatically use the element's DOM title value if available (defaults to false)
16369         */
16370        interceptTitles : false,
16371         /**
16372         * @cfg {Boolean} trackMouse
16373         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
16374         */
16375        trackMouse : false,
16376         /**
16377         * @cfg {Boolean} hideOnClick
16378         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
16379         */
16380        hideOnClick : true,
16381         /**
16382         * @cfg {Number} showDelay
16383         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
16384         */
16385        showDelay : 500,
16386         /**
16387         * @cfg {Number} hideDelay
16388         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
16389         */
16390        hideDelay : 200,
16391         /**
16392         * @cfg {Boolean} autoHide
16393         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
16394         * Used in conjunction with hideDelay.
16395         */
16396        autoHide : true,
16397         /**
16398         * @cfg {Boolean}
16399         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
16400         * (defaults to true).  Used in conjunction with autoDismissDelay.
16401         */
16402        autoDismiss : true,
16403         /**
16404         * @cfg {Number}
16405         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
16406         */
16407        autoDismissDelay : 5000,
16408        /**
16409         * @cfg {Boolean} animate
16410         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
16411         */
16412        animate : false,
16413
16414        /**
16415         * @cfg {String} title
16416         * Title text to display (defaults to '').  This can be any valid HTML markup.
16417         */
16418         title: '',
16419        /**
16420         * @cfg {String} text
16421         * Body text to display (defaults to '').  This can be any valid HTML markup.
16422         */
16423         text : '',
16424        /**
16425         * @cfg {String} cls
16426         * A CSS class to apply to the base quick tip element (defaults to '').
16427         */
16428         cls : '',
16429        /**
16430         * @cfg {Number} width
16431         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
16432         * minWidth or maxWidth.
16433         */
16434         width : null,
16435
16436     /**
16437      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
16438      * or display QuickTips in a page.
16439      */
16440        init : function(){
16441           tm = Roo.QuickTips;
16442           cfg = tm.tagConfig;
16443           if(!inited){
16444               if(!Roo.isReady){ // allow calling of init() before onReady
16445                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
16446                   return;
16447               }
16448               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
16449               el.fxDefaults = {stopFx: true};
16450               // maximum custom styling
16451               //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>');
16452               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>');              
16453               tipTitle = el.child('h3');
16454               tipTitle.enableDisplayMode("block");
16455               tipBody = el.child('div.x-tip-bd');
16456               tipBodyText = el.child('div.x-tip-bd-inner');
16457               //bdLeft = el.child('div.x-tip-bd-left');
16458               //bdRight = el.child('div.x-tip-bd-right');
16459               close = el.child('div.x-tip-close');
16460               close.enableDisplayMode("block");
16461               close.on("click", hide);
16462               var d = Roo.get(document);
16463               d.on("mousedown", onDown);
16464               d.on("mouseover", onOver);
16465               d.on("mouseout", onOut);
16466               d.on("mousemove", onMove);
16467               esc = d.addKeyListener(27, hide);
16468               esc.disable();
16469               if(Roo.dd.DD){
16470                   dd = el.initDD("default", null, {
16471                       onDrag : function(){
16472                           el.sync();  
16473                       }
16474                   });
16475                   dd.setHandleElId(tipTitle.id);
16476                   dd.lock();
16477               }
16478               inited = true;
16479           }
16480           this.enable(); 
16481        },
16482
16483     /**
16484      * Configures a new quick tip instance and assigns it to a target element.  The following config options
16485      * are supported:
16486      * <pre>
16487 Property    Type                   Description
16488 ----------  ---------------------  ------------------------------------------------------------------------
16489 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
16490      * </ul>
16491      * @param {Object} config The config object
16492      */
16493        register : function(config){
16494            var cs = config instanceof Array ? config : arguments;
16495            for(var i = 0, len = cs.length; i < len; i++) {
16496                var c = cs[i];
16497                var target = c.target;
16498                if(target){
16499                    if(target instanceof Array){
16500                        for(var j = 0, jlen = target.length; j < jlen; j++){
16501                            tagEls[target[j]] = c;
16502                        }
16503                    }else{
16504                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
16505                    }
16506                }
16507            }
16508        },
16509
16510     /**
16511      * Removes this quick tip from its element and destroys it.
16512      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
16513      */
16514        unregister : function(el){
16515            delete tagEls[Roo.id(el)];
16516        },
16517
16518     /**
16519      * Enable this quick tip.
16520      */
16521        enable : function(){
16522            if(inited && disabled){
16523                locks.pop();
16524                if(locks.length < 1){
16525                    disabled = false;
16526                }
16527            }
16528        },
16529
16530     /**
16531      * Disable this quick tip.
16532      */
16533        disable : function(){
16534           disabled = true;
16535           clearTimeout(showProc);
16536           clearTimeout(hideProc);
16537           clearTimeout(dismissProc);
16538           if(ce){
16539               hide(true);
16540           }
16541           locks.push(1);
16542        },
16543
16544     /**
16545      * Returns true if the quick tip is enabled, else false.
16546      */
16547        isEnabled : function(){
16548             return !disabled;
16549        },
16550
16551         // private
16552        tagConfig : {
16553            namespace : "ext",
16554            attribute : "qtip",
16555            width : "width",
16556            target : "target",
16557            title : "qtitle",
16558            hide : "hide",
16559            cls : "qclass"
16560        }
16561    };
16562 }();
16563
16564 // backwards compat
16565 Roo.QuickTips.tips = Roo.QuickTips.register;/*
16566  * Based on:
16567  * Ext JS Library 1.1.1
16568  * Copyright(c) 2006-2007, Ext JS, LLC.
16569  *
16570  * Originally Released Under LGPL - original licence link has changed is not relivant.
16571  *
16572  * Fork - LGPL
16573  * <script type="text/javascript">
16574  */
16575  
16576
16577 /**
16578  * @class Roo.tree.TreePanel
16579  * @extends Roo.data.Tree
16580
16581  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
16582  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
16583  * @cfg {Boolean} enableDD true to enable drag and drop
16584  * @cfg {Boolean} enableDrag true to enable just drag
16585  * @cfg {Boolean} enableDrop true to enable just drop
16586  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
16587  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
16588  * @cfg {String} ddGroup The DD group this TreePanel belongs to
16589  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
16590  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
16591  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
16592  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
16593  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
16594  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
16595  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
16596  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
16597  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
16598  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
16599  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
16600  * @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>
16601  * @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>
16602  * 
16603  * @constructor
16604  * @param {String/HTMLElement/Element} el The container element
16605  * @param {Object} config
16606  */
16607 Roo.tree.TreePanel = function(el, config){
16608     var root = false;
16609     var loader = false;
16610     if (config.root) {
16611         root = config.root;
16612         delete config.root;
16613     }
16614     if (config.loader) {
16615         loader = config.loader;
16616         delete config.loader;
16617     }
16618     
16619     Roo.apply(this, config);
16620     Roo.tree.TreePanel.superclass.constructor.call(this);
16621     this.el = Roo.get(el);
16622     this.el.addClass('x-tree');
16623     //console.log(root);
16624     if (root) {
16625         this.setRootNode( Roo.factory(root, Roo.tree));
16626     }
16627     if (loader) {
16628         this.loader = Roo.factory(loader, Roo.tree);
16629     }
16630    /**
16631     * Read-only. The id of the container element becomes this TreePanel's id.
16632     */
16633     this.id = this.el.id;
16634     this.addEvents({
16635         /**
16636         * @event beforeload
16637         * Fires before a node is loaded, return false to cancel
16638         * @param {Node} node The node being loaded
16639         */
16640         "beforeload" : true,
16641         /**
16642         * @event load
16643         * Fires when a node is loaded
16644         * @param {Node} node The node that was loaded
16645         */
16646         "load" : true,
16647         /**
16648         * @event textchange
16649         * Fires when the text for a node is changed
16650         * @param {Node} node The node
16651         * @param {String} text The new text
16652         * @param {String} oldText The old text
16653         */
16654         "textchange" : true,
16655         /**
16656         * @event beforeexpand
16657         * Fires before a node is expanded, return false to cancel.
16658         * @param {Node} node The node
16659         * @param {Boolean} deep
16660         * @param {Boolean} anim
16661         */
16662         "beforeexpand" : true,
16663         /**
16664         * @event beforecollapse
16665         * Fires before a node is collapsed, return false to cancel.
16666         * @param {Node} node The node
16667         * @param {Boolean} deep
16668         * @param {Boolean} anim
16669         */
16670         "beforecollapse" : true,
16671         /**
16672         * @event expand
16673         * Fires when a node is expanded
16674         * @param {Node} node The node
16675         */
16676         "expand" : true,
16677         /**
16678         * @event disabledchange
16679         * Fires when the disabled status of a node changes
16680         * @param {Node} node The node
16681         * @param {Boolean} disabled
16682         */
16683         "disabledchange" : true,
16684         /**
16685         * @event collapse
16686         * Fires when a node is collapsed
16687         * @param {Node} node The node
16688         */
16689         "collapse" : true,
16690         /**
16691         * @event beforeclick
16692         * Fires before click processing on a node. Return false to cancel the default action.
16693         * @param {Node} node The node
16694         * @param {Roo.EventObject} e The event object
16695         */
16696         "beforeclick":true,
16697         /**
16698         * @event checkchange
16699         * Fires when a node with a checkbox's checked property changes
16700         * @param {Node} this This node
16701         * @param {Boolean} checked
16702         */
16703         "checkchange":true,
16704         /**
16705         * @event click
16706         * Fires when a node is clicked
16707         * @param {Node} node The node
16708         * @param {Roo.EventObject} e The event object
16709         */
16710         "click":true,
16711         /**
16712         * @event dblclick
16713         * Fires when a node is double clicked
16714         * @param {Node} node The node
16715         * @param {Roo.EventObject} e The event object
16716         */
16717         "dblclick":true,
16718         /**
16719         * @event contextmenu
16720         * Fires when a node is right clicked
16721         * @param {Node} node The node
16722         * @param {Roo.EventObject} e The event object
16723         */
16724         "contextmenu":true,
16725         /**
16726         * @event beforechildrenrendered
16727         * Fires right before the child nodes for a node are rendered
16728         * @param {Node} node The node
16729         */
16730         "beforechildrenrendered":true,
16731         /**
16732         * @event startdrag
16733         * Fires when a node starts being dragged
16734         * @param {Roo.tree.TreePanel} this
16735         * @param {Roo.tree.TreeNode} node
16736         * @param {event} e The raw browser event
16737         */ 
16738        "startdrag" : true,
16739        /**
16740         * @event enddrag
16741         * Fires when a drag operation is complete
16742         * @param {Roo.tree.TreePanel} this
16743         * @param {Roo.tree.TreeNode} node
16744         * @param {event} e The raw browser event
16745         */
16746        "enddrag" : true,
16747        /**
16748         * @event dragdrop
16749         * Fires when a dragged node is dropped on a valid DD target
16750         * @param {Roo.tree.TreePanel} this
16751         * @param {Roo.tree.TreeNode} node
16752         * @param {DD} dd The dd it was dropped on
16753         * @param {event} e The raw browser event
16754         */
16755        "dragdrop" : true,
16756        /**
16757         * @event beforenodedrop
16758         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16759         * passed to handlers has the following properties:<br />
16760         * <ul style="padding:5px;padding-left:16px;">
16761         * <li>tree - The TreePanel</li>
16762         * <li>target - The node being targeted for the drop</li>
16763         * <li>data - The drag data from the drag source</li>
16764         * <li>point - The point of the drop - append, above or below</li>
16765         * <li>source - The drag source</li>
16766         * <li>rawEvent - Raw mouse event</li>
16767         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16768         * to be inserted by setting them on this object.</li>
16769         * <li>cancel - Set this to true to cancel the drop.</li>
16770         * </ul>
16771         * @param {Object} dropEvent
16772         */
16773        "beforenodedrop" : true,
16774        /**
16775         * @event nodedrop
16776         * Fires after a DD object is dropped on a node in this tree. The dropEvent
16777         * passed to handlers has the following properties:<br />
16778         * <ul style="padding:5px;padding-left:16px;">
16779         * <li>tree - The TreePanel</li>
16780         * <li>target - The node being targeted for the drop</li>
16781         * <li>data - The drag data from the drag source</li>
16782         * <li>point - The point of the drop - append, above or below</li>
16783         * <li>source - The drag source</li>
16784         * <li>rawEvent - Raw mouse event</li>
16785         * <li>dropNode - Dropped node(s).</li>
16786         * </ul>
16787         * @param {Object} dropEvent
16788         */
16789        "nodedrop" : true,
16790         /**
16791         * @event nodedragover
16792         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16793         * passed to handlers has the following properties:<br />
16794         * <ul style="padding:5px;padding-left:16px;">
16795         * <li>tree - The TreePanel</li>
16796         * <li>target - The node being targeted for the drop</li>
16797         * <li>data - The drag data from the drag source</li>
16798         * <li>point - The point of the drop - append, above or below</li>
16799         * <li>source - The drag source</li>
16800         * <li>rawEvent - Raw mouse event</li>
16801         * <li>dropNode - Drop node(s) provided by the source.</li>
16802         * <li>cancel - Set this to true to signal drop not allowed.</li>
16803         * </ul>
16804         * @param {Object} dragOverEvent
16805         */
16806        "nodedragover" : true
16807         
16808     });
16809     if(this.singleExpand){
16810        this.on("beforeexpand", this.restrictExpand, this);
16811     }
16812     if (this.editor) {
16813         this.editor.tree = this;
16814         this.editor = Roo.factory(this.editor, Roo.tree);
16815     }
16816     
16817     if (this.selModel) {
16818         this.selModel = Roo.factory(this.selModel, Roo.tree);
16819     }
16820    
16821 };
16822 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16823     rootVisible : true,
16824     animate: Roo.enableFx,
16825     lines : true,
16826     enableDD : false,
16827     hlDrop : Roo.enableFx,
16828   
16829     renderer: false,
16830     
16831     rendererTip: false,
16832     // private
16833     restrictExpand : function(node){
16834         var p = node.parentNode;
16835         if(p){
16836             if(p.expandedChild && p.expandedChild.parentNode == p){
16837                 p.expandedChild.collapse();
16838             }
16839             p.expandedChild = node;
16840         }
16841     },
16842
16843     // private override
16844     setRootNode : function(node){
16845         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16846         if(!this.rootVisible){
16847             node.ui = new Roo.tree.RootTreeNodeUI(node);
16848         }
16849         return node;
16850     },
16851
16852     /**
16853      * Returns the container element for this TreePanel
16854      */
16855     getEl : function(){
16856         return this.el;
16857     },
16858
16859     /**
16860      * Returns the default TreeLoader for this TreePanel
16861      */
16862     getLoader : function(){
16863         return this.loader;
16864     },
16865
16866     /**
16867      * Expand all nodes
16868      */
16869     expandAll : function(){
16870         this.root.expand(true);
16871     },
16872
16873     /**
16874      * Collapse all nodes
16875      */
16876     collapseAll : function(){
16877         this.root.collapse(true);
16878     },
16879
16880     /**
16881      * Returns the selection model used by this TreePanel
16882      */
16883     getSelectionModel : function(){
16884         if(!this.selModel){
16885             this.selModel = new Roo.tree.DefaultSelectionModel();
16886         }
16887         return this.selModel;
16888     },
16889
16890     /**
16891      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16892      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16893      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16894      * @return {Array}
16895      */
16896     getChecked : function(a, startNode){
16897         startNode = startNode || this.root;
16898         var r = [];
16899         var f = function(){
16900             if(this.attributes.checked){
16901                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16902             }
16903         }
16904         startNode.cascade(f);
16905         return r;
16906     },
16907
16908     /**
16909      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16910      * @param {String} path
16911      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16912      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16913      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16914      */
16915     expandPath : function(path, attr, callback){
16916         attr = attr || "id";
16917         var keys = path.split(this.pathSeparator);
16918         var curNode = this.root;
16919         if(curNode.attributes[attr] != keys[1]){ // invalid root
16920             if(callback){
16921                 callback(false, null);
16922             }
16923             return;
16924         }
16925         var index = 1;
16926         var f = function(){
16927             if(++index == keys.length){
16928                 if(callback){
16929                     callback(true, curNode);
16930                 }
16931                 return;
16932             }
16933             var c = curNode.findChild(attr, keys[index]);
16934             if(!c){
16935                 if(callback){
16936                     callback(false, curNode);
16937                 }
16938                 return;
16939             }
16940             curNode = c;
16941             c.expand(false, false, f);
16942         };
16943         curNode.expand(false, false, f);
16944     },
16945
16946     /**
16947      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16948      * @param {String} path
16949      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16950      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16951      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16952      */
16953     selectPath : function(path, attr, callback){
16954         attr = attr || "id";
16955         var keys = path.split(this.pathSeparator);
16956         var v = keys.pop();
16957         if(keys.length > 0){
16958             var f = function(success, node){
16959                 if(success && node){
16960                     var n = node.findChild(attr, v);
16961                     if(n){
16962                         n.select();
16963                         if(callback){
16964                             callback(true, n);
16965                         }
16966                     }else if(callback){
16967                         callback(false, n);
16968                     }
16969                 }else{
16970                     if(callback){
16971                         callback(false, n);
16972                     }
16973                 }
16974             };
16975             this.expandPath(keys.join(this.pathSeparator), attr, f);
16976         }else{
16977             this.root.select();
16978             if(callback){
16979                 callback(true, this.root);
16980             }
16981         }
16982     },
16983
16984     getTreeEl : function(){
16985         return this.el;
16986     },
16987
16988     /**
16989      * Trigger rendering of this TreePanel
16990      */
16991     render : function(){
16992         if (this.innerCt) {
16993             return this; // stop it rendering more than once!!
16994         }
16995         
16996         this.innerCt = this.el.createChild({tag:"ul",
16997                cls:"x-tree-root-ct " +
16998                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
16999
17000         if(this.containerScroll){
17001             Roo.dd.ScrollManager.register(this.el);
17002         }
17003         if((this.enableDD || this.enableDrop) && !this.dropZone){
17004            /**
17005             * The dropZone used by this tree if drop is enabled
17006             * @type Roo.tree.TreeDropZone
17007             */
17008              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
17009                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
17010            });
17011         }
17012         if((this.enableDD || this.enableDrag) && !this.dragZone){
17013            /**
17014             * The dragZone used by this tree if drag is enabled
17015             * @type Roo.tree.TreeDragZone
17016             */
17017             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
17018                ddGroup: this.ddGroup || "TreeDD",
17019                scroll: this.ddScroll
17020            });
17021         }
17022         this.getSelectionModel().init(this);
17023         if (!this.root) {
17024             Roo.log("ROOT not set in tree");
17025             return this;
17026         }
17027         this.root.render();
17028         if(!this.rootVisible){
17029             this.root.renderChildren();
17030         }
17031         return this;
17032     }
17033 });/*
17034  * Based on:
17035  * Ext JS Library 1.1.1
17036  * Copyright(c) 2006-2007, Ext JS, LLC.
17037  *
17038  * Originally Released Under LGPL - original licence link has changed is not relivant.
17039  *
17040  * Fork - LGPL
17041  * <script type="text/javascript">
17042  */
17043  
17044
17045 /**
17046  * @class Roo.tree.DefaultSelectionModel
17047  * @extends Roo.util.Observable
17048  * The default single selection for a TreePanel.
17049  * @param {Object} cfg Configuration
17050  */
17051 Roo.tree.DefaultSelectionModel = function(cfg){
17052    this.selNode = null;
17053    
17054    
17055    
17056    this.addEvents({
17057        /**
17058         * @event selectionchange
17059         * Fires when the selected node changes
17060         * @param {DefaultSelectionModel} this
17061         * @param {TreeNode} node the new selection
17062         */
17063        "selectionchange" : true,
17064
17065        /**
17066         * @event beforeselect
17067         * Fires before the selected node changes, return false to cancel the change
17068         * @param {DefaultSelectionModel} this
17069         * @param {TreeNode} node the new selection
17070         * @param {TreeNode} node the old selection
17071         */
17072        "beforeselect" : true
17073    });
17074    
17075     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
17076 };
17077
17078 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
17079     init : function(tree){
17080         this.tree = tree;
17081         tree.getTreeEl().on("keydown", this.onKeyDown, this);
17082         tree.on("click", this.onNodeClick, this);
17083     },
17084     
17085     onNodeClick : function(node, e){
17086         if (e.ctrlKey && this.selNode == node)  {
17087             this.unselect(node);
17088             return;
17089         }
17090         this.select(node);
17091     },
17092     
17093     /**
17094      * Select a node.
17095      * @param {TreeNode} node The node to select
17096      * @return {TreeNode} The selected node
17097      */
17098     select : function(node){
17099         var last = this.selNode;
17100         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
17101             if(last){
17102                 last.ui.onSelectedChange(false);
17103             }
17104             this.selNode = node;
17105             node.ui.onSelectedChange(true);
17106             this.fireEvent("selectionchange", this, node, last);
17107         }
17108         return node;
17109     },
17110     
17111     /**
17112      * Deselect a node.
17113      * @param {TreeNode} node The node to unselect
17114      */
17115     unselect : function(node){
17116         if(this.selNode == node){
17117             this.clearSelections();
17118         }    
17119     },
17120     
17121     /**
17122      * Clear all selections
17123      */
17124     clearSelections : function(){
17125         var n = this.selNode;
17126         if(n){
17127             n.ui.onSelectedChange(false);
17128             this.selNode = null;
17129             this.fireEvent("selectionchange", this, null);
17130         }
17131         return n;
17132     },
17133     
17134     /**
17135      * Get the selected node
17136      * @return {TreeNode} The selected node
17137      */
17138     getSelectedNode : function(){
17139         return this.selNode;    
17140     },
17141     
17142     /**
17143      * Returns true if the node is selected
17144      * @param {TreeNode} node The node to check
17145      * @return {Boolean}
17146      */
17147     isSelected : function(node){
17148         return this.selNode == node;  
17149     },
17150
17151     /**
17152      * Selects the node above the selected node in the tree, intelligently walking the nodes
17153      * @return TreeNode The new selection
17154      */
17155     selectPrevious : function(){
17156         var s = this.selNode || this.lastSelNode;
17157         if(!s){
17158             return null;
17159         }
17160         var ps = s.previousSibling;
17161         if(ps){
17162             if(!ps.isExpanded() || ps.childNodes.length < 1){
17163                 return this.select(ps);
17164             } else{
17165                 var lc = ps.lastChild;
17166                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
17167                     lc = lc.lastChild;
17168                 }
17169                 return this.select(lc);
17170             }
17171         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
17172             return this.select(s.parentNode);
17173         }
17174         return null;
17175     },
17176
17177     /**
17178      * Selects the node above the selected node in the tree, intelligently walking the nodes
17179      * @return TreeNode The new selection
17180      */
17181     selectNext : function(){
17182         var s = this.selNode || this.lastSelNode;
17183         if(!s){
17184             return null;
17185         }
17186         if(s.firstChild && s.isExpanded()){
17187              return this.select(s.firstChild);
17188          }else if(s.nextSibling){
17189              return this.select(s.nextSibling);
17190          }else if(s.parentNode){
17191             var newS = null;
17192             s.parentNode.bubble(function(){
17193                 if(this.nextSibling){
17194                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
17195                     return false;
17196                 }
17197             });
17198             return newS;
17199          }
17200         return null;
17201     },
17202
17203     onKeyDown : function(e){
17204         var s = this.selNode || this.lastSelNode;
17205         // undesirable, but required
17206         var sm = this;
17207         if(!s){
17208             return;
17209         }
17210         var k = e.getKey();
17211         switch(k){
17212              case e.DOWN:
17213                  e.stopEvent();
17214                  this.selectNext();
17215              break;
17216              case e.UP:
17217                  e.stopEvent();
17218                  this.selectPrevious();
17219              break;
17220              case e.RIGHT:
17221                  e.preventDefault();
17222                  if(s.hasChildNodes()){
17223                      if(!s.isExpanded()){
17224                          s.expand();
17225                      }else if(s.firstChild){
17226                          this.select(s.firstChild, e);
17227                      }
17228                  }
17229              break;
17230              case e.LEFT:
17231                  e.preventDefault();
17232                  if(s.hasChildNodes() && s.isExpanded()){
17233                      s.collapse();
17234                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
17235                      this.select(s.parentNode, e);
17236                  }
17237              break;
17238         };
17239     }
17240 });
17241
17242 /**
17243  * @class Roo.tree.MultiSelectionModel
17244  * @extends Roo.util.Observable
17245  * Multi selection for a TreePanel.
17246  * @param {Object} cfg Configuration
17247  */
17248 Roo.tree.MultiSelectionModel = function(){
17249    this.selNodes = [];
17250    this.selMap = {};
17251    this.addEvents({
17252        /**
17253         * @event selectionchange
17254         * Fires when the selected nodes change
17255         * @param {MultiSelectionModel} this
17256         * @param {Array} nodes Array of the selected nodes
17257         */
17258        "selectionchange" : true
17259    });
17260    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
17261    
17262 };
17263
17264 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
17265     init : function(tree){
17266         this.tree = tree;
17267         tree.getTreeEl().on("keydown", this.onKeyDown, this);
17268         tree.on("click", this.onNodeClick, this);
17269     },
17270     
17271     onNodeClick : function(node, e){
17272         this.select(node, e, e.ctrlKey);
17273     },
17274     
17275     /**
17276      * Select a node.
17277      * @param {TreeNode} node The node to select
17278      * @param {EventObject} e (optional) An event associated with the selection
17279      * @param {Boolean} keepExisting True to retain existing selections
17280      * @return {TreeNode} The selected node
17281      */
17282     select : function(node, e, keepExisting){
17283         if(keepExisting !== true){
17284             this.clearSelections(true);
17285         }
17286         if(this.isSelected(node)){
17287             this.lastSelNode = node;
17288             return node;
17289         }
17290         this.selNodes.push(node);
17291         this.selMap[node.id] = node;
17292         this.lastSelNode = node;
17293         node.ui.onSelectedChange(true);
17294         this.fireEvent("selectionchange", this, this.selNodes);
17295         return node;
17296     },
17297     
17298     /**
17299      * Deselect a node.
17300      * @param {TreeNode} node The node to unselect
17301      */
17302     unselect : function(node){
17303         if(this.selMap[node.id]){
17304             node.ui.onSelectedChange(false);
17305             var sn = this.selNodes;
17306             var index = -1;
17307             if(sn.indexOf){
17308                 index = sn.indexOf(node);
17309             }else{
17310                 for(var i = 0, len = sn.length; i < len; i++){
17311                     if(sn[i] == node){
17312                         index = i;
17313                         break;
17314                     }
17315                 }
17316             }
17317             if(index != -1){
17318                 this.selNodes.splice(index, 1);
17319             }
17320             delete this.selMap[node.id];
17321             this.fireEvent("selectionchange", this, this.selNodes);
17322         }
17323     },
17324     
17325     /**
17326      * Clear all selections
17327      */
17328     clearSelections : function(suppressEvent){
17329         var sn = this.selNodes;
17330         if(sn.length > 0){
17331             for(var i = 0, len = sn.length; i < len; i++){
17332                 sn[i].ui.onSelectedChange(false);
17333             }
17334             this.selNodes = [];
17335             this.selMap = {};
17336             if(suppressEvent !== true){
17337                 this.fireEvent("selectionchange", this, this.selNodes);
17338             }
17339         }
17340     },
17341     
17342     /**
17343      * Returns true if the node is selected
17344      * @param {TreeNode} node The node to check
17345      * @return {Boolean}
17346      */
17347     isSelected : function(node){
17348         return this.selMap[node.id] ? true : false;  
17349     },
17350     
17351     /**
17352      * Returns an array of the selected nodes
17353      * @return {Array}
17354      */
17355     getSelectedNodes : function(){
17356         return this.selNodes;    
17357     },
17358
17359     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
17360
17361     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
17362
17363     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
17364 });/*
17365  * Based on:
17366  * Ext JS Library 1.1.1
17367  * Copyright(c) 2006-2007, Ext JS, LLC.
17368  *
17369  * Originally Released Under LGPL - original licence link has changed is not relivant.
17370  *
17371  * Fork - LGPL
17372  * <script type="text/javascript">
17373  */
17374  
17375 /**
17376  * @class Roo.tree.TreeNode
17377  * @extends Roo.data.Node
17378  * @cfg {String} text The text for this node
17379  * @cfg {Boolean} expanded true to start the node expanded
17380  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
17381  * @cfg {Boolean} allowDrop false if this node cannot be drop on
17382  * @cfg {Boolean} disabled true to start the node disabled
17383  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
17384  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
17385  * @cfg {String} cls A css class to be added to the node
17386  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
17387  * @cfg {String} href URL of the link used for the node (defaults to #)
17388  * @cfg {String} hrefTarget target frame for the link
17389  * @cfg {String} qtip An Ext QuickTip for the node
17390  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
17391  * @cfg {Boolean} singleClickExpand True for single click expand on this node
17392  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
17393  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
17394  * (defaults to undefined with no checkbox rendered)
17395  * @constructor
17396  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
17397  */
17398 Roo.tree.TreeNode = function(attributes){
17399     attributes = attributes || {};
17400     if(typeof attributes == "string"){
17401         attributes = {text: attributes};
17402     }
17403     this.childrenRendered = false;
17404     this.rendered = false;
17405     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
17406     this.expanded = attributes.expanded === true;
17407     this.isTarget = attributes.isTarget !== false;
17408     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
17409     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
17410
17411     /**
17412      * Read-only. The text for this node. To change it use setText().
17413      * @type String
17414      */
17415     this.text = attributes.text;
17416     /**
17417      * True if this node is disabled.
17418      * @type Boolean
17419      */
17420     this.disabled = attributes.disabled === true;
17421
17422     this.addEvents({
17423         /**
17424         * @event textchange
17425         * Fires when the text for this node is changed
17426         * @param {Node} this This node
17427         * @param {String} text The new text
17428         * @param {String} oldText The old text
17429         */
17430         "textchange" : true,
17431         /**
17432         * @event beforeexpand
17433         * Fires before this node is expanded, return false to cancel.
17434         * @param {Node} this This node
17435         * @param {Boolean} deep
17436         * @param {Boolean} anim
17437         */
17438         "beforeexpand" : true,
17439         /**
17440         * @event beforecollapse
17441         * Fires before this node is collapsed, return false to cancel.
17442         * @param {Node} this This node
17443         * @param {Boolean} deep
17444         * @param {Boolean} anim
17445         */
17446         "beforecollapse" : true,
17447         /**
17448         * @event expand
17449         * Fires when this node is expanded
17450         * @param {Node} this This node
17451         */
17452         "expand" : true,
17453         /**
17454         * @event disabledchange
17455         * Fires when the disabled status of this node changes
17456         * @param {Node} this This node
17457         * @param {Boolean} disabled
17458         */
17459         "disabledchange" : true,
17460         /**
17461         * @event collapse
17462         * Fires when this node is collapsed
17463         * @param {Node} this This node
17464         */
17465         "collapse" : true,
17466         /**
17467         * @event beforeclick
17468         * Fires before click processing. Return false to cancel the default action.
17469         * @param {Node} this This node
17470         * @param {Roo.EventObject} e The event object
17471         */
17472         "beforeclick":true,
17473         /**
17474         * @event checkchange
17475         * Fires when a node with a checkbox's checked property changes
17476         * @param {Node} this This node
17477         * @param {Boolean} checked
17478         */
17479         "checkchange":true,
17480         /**
17481         * @event click
17482         * Fires when this node is clicked
17483         * @param {Node} this This node
17484         * @param {Roo.EventObject} e The event object
17485         */
17486         "click":true,
17487         /**
17488         * @event dblclick
17489         * Fires when this node is double clicked
17490         * @param {Node} this This node
17491         * @param {Roo.EventObject} e The event object
17492         */
17493         "dblclick":true,
17494         /**
17495         * @event contextmenu
17496         * Fires when this node is right clicked
17497         * @param {Node} this This node
17498         * @param {Roo.EventObject} e The event object
17499         */
17500         "contextmenu":true,
17501         /**
17502         * @event beforechildrenrendered
17503         * Fires right before the child nodes for this node are rendered
17504         * @param {Node} this This node
17505         */
17506         "beforechildrenrendered":true
17507     });
17508
17509     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
17510
17511     /**
17512      * Read-only. The UI for this node
17513      * @type TreeNodeUI
17514      */
17515     this.ui = new uiClass(this);
17516     
17517     // finally support items[]
17518     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
17519         return;
17520     }
17521     
17522     
17523     Roo.each(this.attributes.items, function(c) {
17524         this.appendChild(Roo.factory(c,Roo.Tree));
17525     }, this);
17526     delete this.attributes.items;
17527     
17528     
17529     
17530 };
17531 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
17532     preventHScroll: true,
17533     /**
17534      * Returns true if this node is expanded
17535      * @return {Boolean}
17536      */
17537     isExpanded : function(){
17538         return this.expanded;
17539     },
17540
17541     /**
17542      * Returns the UI object for this node
17543      * @return {TreeNodeUI}
17544      */
17545     getUI : function(){
17546         return this.ui;
17547     },
17548
17549     // private override
17550     setFirstChild : function(node){
17551         var of = this.firstChild;
17552         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
17553         if(this.childrenRendered && of && node != of){
17554             of.renderIndent(true, true);
17555         }
17556         if(this.rendered){
17557             this.renderIndent(true, true);
17558         }
17559     },
17560
17561     // private override
17562     setLastChild : function(node){
17563         var ol = this.lastChild;
17564         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
17565         if(this.childrenRendered && ol && node != ol){
17566             ol.renderIndent(true, true);
17567         }
17568         if(this.rendered){
17569             this.renderIndent(true, true);
17570         }
17571     },
17572
17573     // these methods are overridden to provide lazy rendering support
17574     // private override
17575     appendChild : function()
17576     {
17577         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
17578         if(node && this.childrenRendered){
17579             node.render();
17580         }
17581         this.ui.updateExpandIcon();
17582         return node;
17583     },
17584
17585     // private override
17586     removeChild : function(node){
17587         this.ownerTree.getSelectionModel().unselect(node);
17588         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
17589         // if it's been rendered remove dom node
17590         if(this.childrenRendered){
17591             node.ui.remove();
17592         }
17593         if(this.childNodes.length < 1){
17594             this.collapse(false, false);
17595         }else{
17596             this.ui.updateExpandIcon();
17597         }
17598         if(!this.firstChild) {
17599             this.childrenRendered = false;
17600         }
17601         return node;
17602     },
17603
17604     // private override
17605     insertBefore : function(node, refNode){
17606         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
17607         if(newNode && refNode && this.childrenRendered){
17608             node.render();
17609         }
17610         this.ui.updateExpandIcon();
17611         return newNode;
17612     },
17613
17614     /**
17615      * Sets the text for this node
17616      * @param {String} text
17617      */
17618     setText : function(text){
17619         var oldText = this.text;
17620         this.text = text;
17621         this.attributes.text = text;
17622         if(this.rendered){ // event without subscribing
17623             this.ui.onTextChange(this, text, oldText);
17624         }
17625         this.fireEvent("textchange", this, text, oldText);
17626     },
17627
17628     /**
17629      * Triggers selection of this node
17630      */
17631     select : function(){
17632         this.getOwnerTree().getSelectionModel().select(this);
17633     },
17634
17635     /**
17636      * Triggers deselection of this node
17637      */
17638     unselect : function(){
17639         this.getOwnerTree().getSelectionModel().unselect(this);
17640     },
17641
17642     /**
17643      * Returns true if this node is selected
17644      * @return {Boolean}
17645      */
17646     isSelected : function(){
17647         return this.getOwnerTree().getSelectionModel().isSelected(this);
17648     },
17649
17650     /**
17651      * Expand this node.
17652      * @param {Boolean} deep (optional) True to expand all children as well
17653      * @param {Boolean} anim (optional) false to cancel the default animation
17654      * @param {Function} callback (optional) A callback to be called when
17655      * expanding this node completes (does not wait for deep expand to complete).
17656      * Called with 1 parameter, this node.
17657      */
17658     expand : function(deep, anim, callback){
17659         if(!this.expanded){
17660             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
17661                 return;
17662             }
17663             if(!this.childrenRendered){
17664                 this.renderChildren();
17665             }
17666             this.expanded = true;
17667             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17668                 this.ui.animExpand(function(){
17669                     this.fireEvent("expand", this);
17670                     if(typeof callback == "function"){
17671                         callback(this);
17672                     }
17673                     if(deep === true){
17674                         this.expandChildNodes(true);
17675                     }
17676                 }.createDelegate(this));
17677                 return;
17678             }else{
17679                 this.ui.expand();
17680                 this.fireEvent("expand", this);
17681                 if(typeof callback == "function"){
17682                     callback(this);
17683                 }
17684             }
17685         }else{
17686            if(typeof callback == "function"){
17687                callback(this);
17688            }
17689         }
17690         if(deep === true){
17691             this.expandChildNodes(true);
17692         }
17693     },
17694
17695     isHiddenRoot : function(){
17696         return this.isRoot && !this.getOwnerTree().rootVisible;
17697     },
17698
17699     /**
17700      * Collapse this node.
17701      * @param {Boolean} deep (optional) True to collapse all children as well
17702      * @param {Boolean} anim (optional) false to cancel the default animation
17703      */
17704     collapse : function(deep, anim){
17705         if(this.expanded && !this.isHiddenRoot()){
17706             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17707                 return;
17708             }
17709             this.expanded = false;
17710             if((this.getOwnerTree().animate && anim !== false) || anim){
17711                 this.ui.animCollapse(function(){
17712                     this.fireEvent("collapse", this);
17713                     if(deep === true){
17714                         this.collapseChildNodes(true);
17715                     }
17716                 }.createDelegate(this));
17717                 return;
17718             }else{
17719                 this.ui.collapse();
17720                 this.fireEvent("collapse", this);
17721             }
17722         }
17723         if(deep === true){
17724             var cs = this.childNodes;
17725             for(var i = 0, len = cs.length; i < len; i++) {
17726                 cs[i].collapse(true, false);
17727             }
17728         }
17729     },
17730
17731     // private
17732     delayedExpand : function(delay){
17733         if(!this.expandProcId){
17734             this.expandProcId = this.expand.defer(delay, this);
17735         }
17736     },
17737
17738     // private
17739     cancelExpand : function(){
17740         if(this.expandProcId){
17741             clearTimeout(this.expandProcId);
17742         }
17743         this.expandProcId = false;
17744     },
17745
17746     /**
17747      * Toggles expanded/collapsed state of the node
17748      */
17749     toggle : function(){
17750         if(this.expanded){
17751             this.collapse();
17752         }else{
17753             this.expand();
17754         }
17755     },
17756
17757     /**
17758      * Ensures all parent nodes are expanded
17759      */
17760     ensureVisible : function(callback){
17761         var tree = this.getOwnerTree();
17762         tree.expandPath(this.parentNode.getPath(), false, function(){
17763             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17764             Roo.callback(callback);
17765         }.createDelegate(this));
17766     },
17767
17768     /**
17769      * Expand all child nodes
17770      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17771      */
17772     expandChildNodes : function(deep){
17773         var cs = this.childNodes;
17774         for(var i = 0, len = cs.length; i < len; i++) {
17775                 cs[i].expand(deep);
17776         }
17777     },
17778
17779     /**
17780      * Collapse all child nodes
17781      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17782      */
17783     collapseChildNodes : function(deep){
17784         var cs = this.childNodes;
17785         for(var i = 0, len = cs.length; i < len; i++) {
17786                 cs[i].collapse(deep);
17787         }
17788     },
17789
17790     /**
17791      * Disables this node
17792      */
17793     disable : function(){
17794         this.disabled = true;
17795         this.unselect();
17796         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17797             this.ui.onDisableChange(this, true);
17798         }
17799         this.fireEvent("disabledchange", this, true);
17800     },
17801
17802     /**
17803      * Enables this node
17804      */
17805     enable : function(){
17806         this.disabled = false;
17807         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17808             this.ui.onDisableChange(this, false);
17809         }
17810         this.fireEvent("disabledchange", this, false);
17811     },
17812
17813     // private
17814     renderChildren : function(suppressEvent){
17815         if(suppressEvent !== false){
17816             this.fireEvent("beforechildrenrendered", this);
17817         }
17818         var cs = this.childNodes;
17819         for(var i = 0, len = cs.length; i < len; i++){
17820             cs[i].render(true);
17821         }
17822         this.childrenRendered = true;
17823     },
17824
17825     // private
17826     sort : function(fn, scope){
17827         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17828         if(this.childrenRendered){
17829             var cs = this.childNodes;
17830             for(var i = 0, len = cs.length; i < len; i++){
17831                 cs[i].render(true);
17832             }
17833         }
17834     },
17835
17836     // private
17837     render : function(bulkRender){
17838         this.ui.render(bulkRender);
17839         if(!this.rendered){
17840             this.rendered = true;
17841             if(this.expanded){
17842                 this.expanded = false;
17843                 this.expand(false, false);
17844             }
17845         }
17846     },
17847
17848     // private
17849     renderIndent : function(deep, refresh){
17850         if(refresh){
17851             this.ui.childIndent = null;
17852         }
17853         this.ui.renderIndent();
17854         if(deep === true && this.childrenRendered){
17855             var cs = this.childNodes;
17856             for(var i = 0, len = cs.length; i < len; i++){
17857                 cs[i].renderIndent(true, refresh);
17858             }
17859         }
17860     }
17861 });/*
17862  * Based on:
17863  * Ext JS Library 1.1.1
17864  * Copyright(c) 2006-2007, Ext JS, LLC.
17865  *
17866  * Originally Released Under LGPL - original licence link has changed is not relivant.
17867  *
17868  * Fork - LGPL
17869  * <script type="text/javascript">
17870  */
17871  
17872 /**
17873  * @class Roo.tree.AsyncTreeNode
17874  * @extends Roo.tree.TreeNode
17875  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17876  * @constructor
17877  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
17878  */
17879  Roo.tree.AsyncTreeNode = function(config){
17880     this.loaded = false;
17881     this.loading = false;
17882     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17883     /**
17884     * @event beforeload
17885     * Fires before this node is loaded, return false to cancel
17886     * @param {Node} this This node
17887     */
17888     this.addEvents({'beforeload':true, 'load': true});
17889     /**
17890     * @event load
17891     * Fires when this node is loaded
17892     * @param {Node} this This node
17893     */
17894     /**
17895      * The loader used by this node (defaults to using the tree's defined loader)
17896      * @type TreeLoader
17897      * @property loader
17898      */
17899 };
17900 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17901     expand : function(deep, anim, callback){
17902         if(this.loading){ // if an async load is already running, waiting til it's done
17903             var timer;
17904             var f = function(){
17905                 if(!this.loading){ // done loading
17906                     clearInterval(timer);
17907                     this.expand(deep, anim, callback);
17908                 }
17909             }.createDelegate(this);
17910             timer = setInterval(f, 200);
17911             return;
17912         }
17913         if(!this.loaded){
17914             if(this.fireEvent("beforeload", this) === false){
17915                 return;
17916             }
17917             this.loading = true;
17918             this.ui.beforeLoad(this);
17919             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17920             if(loader){
17921                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17922                 return;
17923             }
17924         }
17925         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17926     },
17927     
17928     /**
17929      * Returns true if this node is currently loading
17930      * @return {Boolean}
17931      */
17932     isLoading : function(){
17933         return this.loading;  
17934     },
17935     
17936     loadComplete : function(deep, anim, callback){
17937         this.loading = false;
17938         this.loaded = true;
17939         this.ui.afterLoad(this);
17940         this.fireEvent("load", this);
17941         this.expand(deep, anim, callback);
17942     },
17943     
17944     /**
17945      * Returns true if this node has been loaded
17946      * @return {Boolean}
17947      */
17948     isLoaded : function(){
17949         return this.loaded;
17950     },
17951     
17952     hasChildNodes : function(){
17953         if(!this.isLeaf() && !this.loaded){
17954             return true;
17955         }else{
17956             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17957         }
17958     },
17959
17960     /**
17961      * Trigger a reload for this node
17962      * @param {Function} callback
17963      */
17964     reload : function(callback){
17965         this.collapse(false, false);
17966         while(this.firstChild){
17967             this.removeChild(this.firstChild);
17968         }
17969         this.childrenRendered = false;
17970         this.loaded = false;
17971         if(this.isHiddenRoot()){
17972             this.expanded = false;
17973         }
17974         this.expand(false, false, callback);
17975     }
17976 });/*
17977  * Based on:
17978  * Ext JS Library 1.1.1
17979  * Copyright(c) 2006-2007, Ext JS, LLC.
17980  *
17981  * Originally Released Under LGPL - original licence link has changed is not relivant.
17982  *
17983  * Fork - LGPL
17984  * <script type="text/javascript">
17985  */
17986  
17987 /**
17988  * @class Roo.tree.TreeNodeUI
17989  * @constructor
17990  * @param {Object} node The node to render
17991  * The TreeNode UI implementation is separate from the
17992  * tree implementation. Unless you are customizing the tree UI,
17993  * you should never have to use this directly.
17994  */
17995 Roo.tree.TreeNodeUI = function(node){
17996     this.node = node;
17997     this.rendered = false;
17998     this.animating = false;
17999     this.emptyIcon = Roo.BLANK_IMAGE_URL;
18000 };
18001
18002 Roo.tree.TreeNodeUI.prototype = {
18003     removeChild : function(node){
18004         if(this.rendered){
18005             this.ctNode.removeChild(node.ui.getEl());
18006         }
18007     },
18008
18009     beforeLoad : function(){
18010          this.addClass("x-tree-node-loading");
18011     },
18012
18013     afterLoad : function(){
18014          this.removeClass("x-tree-node-loading");
18015     },
18016
18017     onTextChange : function(node, text, oldText){
18018         if(this.rendered){
18019             this.textNode.innerHTML = text;
18020         }
18021     },
18022
18023     onDisableChange : function(node, state){
18024         this.disabled = state;
18025         if(state){
18026             this.addClass("x-tree-node-disabled");
18027         }else{
18028             this.removeClass("x-tree-node-disabled");
18029         }
18030     },
18031
18032     onSelectedChange : function(state){
18033         if(state){
18034             this.focus();
18035             this.addClass("x-tree-selected");
18036         }else{
18037             //this.blur();
18038             this.removeClass("x-tree-selected");
18039         }
18040     },
18041
18042     onMove : function(tree, node, oldParent, newParent, index, refNode){
18043         this.childIndent = null;
18044         if(this.rendered){
18045             var targetNode = newParent.ui.getContainer();
18046             if(!targetNode){//target not rendered
18047                 this.holder = document.createElement("div");
18048                 this.holder.appendChild(this.wrap);
18049                 return;
18050             }
18051             var insertBefore = refNode ? refNode.ui.getEl() : null;
18052             if(insertBefore){
18053                 targetNode.insertBefore(this.wrap, insertBefore);
18054             }else{
18055                 targetNode.appendChild(this.wrap);
18056             }
18057             this.node.renderIndent(true);
18058         }
18059     },
18060
18061     addClass : function(cls){
18062         if(this.elNode){
18063             Roo.fly(this.elNode).addClass(cls);
18064         }
18065     },
18066
18067     removeClass : function(cls){
18068         if(this.elNode){
18069             Roo.fly(this.elNode).removeClass(cls);
18070         }
18071     },
18072
18073     remove : function(){
18074         if(this.rendered){
18075             this.holder = document.createElement("div");
18076             this.holder.appendChild(this.wrap);
18077         }
18078     },
18079
18080     fireEvent : function(){
18081         return this.node.fireEvent.apply(this.node, arguments);
18082     },
18083
18084     initEvents : function(){
18085         this.node.on("move", this.onMove, this);
18086         var E = Roo.EventManager;
18087         var a = this.anchor;
18088
18089         var el = Roo.fly(a, '_treeui');
18090
18091         if(Roo.isOpera){ // opera render bug ignores the CSS
18092             el.setStyle("text-decoration", "none");
18093         }
18094
18095         el.on("click", this.onClick, this);
18096         el.on("dblclick", this.onDblClick, this);
18097
18098         if(this.checkbox){
18099             Roo.EventManager.on(this.checkbox,
18100                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
18101         }
18102
18103         el.on("contextmenu", this.onContextMenu, this);
18104
18105         var icon = Roo.fly(this.iconNode);
18106         icon.on("click", this.onClick, this);
18107         icon.on("dblclick", this.onDblClick, this);
18108         icon.on("contextmenu", this.onContextMenu, this);
18109         E.on(this.ecNode, "click", this.ecClick, this, true);
18110
18111         if(this.node.disabled){
18112             this.addClass("x-tree-node-disabled");
18113         }
18114         if(this.node.hidden){
18115             this.addClass("x-tree-node-disabled");
18116         }
18117         var ot = this.node.getOwnerTree();
18118         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
18119         if(dd && (!this.node.isRoot || ot.rootVisible)){
18120             Roo.dd.Registry.register(this.elNode, {
18121                 node: this.node,
18122                 handles: this.getDDHandles(),
18123                 isHandle: false
18124             });
18125         }
18126     },
18127
18128     getDDHandles : function(){
18129         return [this.iconNode, this.textNode];
18130     },
18131
18132     hide : function(){
18133         if(this.rendered){
18134             this.wrap.style.display = "none";
18135         }
18136     },
18137
18138     show : function(){
18139         if(this.rendered){
18140             this.wrap.style.display = "";
18141         }
18142     },
18143
18144     onContextMenu : function(e){
18145         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
18146             e.preventDefault();
18147             this.focus();
18148             this.fireEvent("contextmenu", this.node, e);
18149         }
18150     },
18151
18152     onClick : function(e){
18153         if(this.dropping){
18154             e.stopEvent();
18155             return;
18156         }
18157         if(this.fireEvent("beforeclick", this.node, e) !== false){
18158             if(!this.disabled && this.node.attributes.href){
18159                 this.fireEvent("click", this.node, e);
18160                 return;
18161             }
18162             e.preventDefault();
18163             if(this.disabled){
18164                 return;
18165             }
18166
18167             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
18168                 this.node.toggle();
18169             }
18170
18171             this.fireEvent("click", this.node, e);
18172         }else{
18173             e.stopEvent();
18174         }
18175     },
18176
18177     onDblClick : function(e){
18178         e.preventDefault();
18179         if(this.disabled){
18180             return;
18181         }
18182         if(this.checkbox){
18183             this.toggleCheck();
18184         }
18185         if(!this.animating && this.node.hasChildNodes()){
18186             this.node.toggle();
18187         }
18188         this.fireEvent("dblclick", this.node, e);
18189     },
18190
18191     onCheckChange : function(){
18192         var checked = this.checkbox.checked;
18193         this.node.attributes.checked = checked;
18194         this.fireEvent('checkchange', this.node, checked);
18195     },
18196
18197     ecClick : function(e){
18198         if(!this.animating && this.node.hasChildNodes()){
18199             this.node.toggle();
18200         }
18201     },
18202
18203     startDrop : function(){
18204         this.dropping = true;
18205     },
18206
18207     // delayed drop so the click event doesn't get fired on a drop
18208     endDrop : function(){
18209        setTimeout(function(){
18210            this.dropping = false;
18211        }.createDelegate(this), 50);
18212     },
18213
18214     expand : function(){
18215         this.updateExpandIcon();
18216         this.ctNode.style.display = "";
18217     },
18218
18219     focus : function(){
18220         if(!this.node.preventHScroll){
18221             try{this.anchor.focus();
18222             }catch(e){}
18223         }else if(!Roo.isIE){
18224             try{
18225                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
18226                 var l = noscroll.scrollLeft;
18227                 this.anchor.focus();
18228                 noscroll.scrollLeft = l;
18229             }catch(e){}
18230         }
18231     },
18232
18233     toggleCheck : function(value){
18234         var cb = this.checkbox;
18235         if(cb){
18236             cb.checked = (value === undefined ? !cb.checked : value);
18237         }
18238     },
18239
18240     blur : function(){
18241         try{
18242             this.anchor.blur();
18243         }catch(e){}
18244     },
18245
18246     animExpand : function(callback){
18247         var ct = Roo.get(this.ctNode);
18248         ct.stopFx();
18249         if(!this.node.hasChildNodes()){
18250             this.updateExpandIcon();
18251             this.ctNode.style.display = "";
18252             Roo.callback(callback);
18253             return;
18254         }
18255         this.animating = true;
18256         this.updateExpandIcon();
18257
18258         ct.slideIn('t', {
18259            callback : function(){
18260                this.animating = false;
18261                Roo.callback(callback);
18262             },
18263             scope: this,
18264             duration: this.node.ownerTree.duration || .25
18265         });
18266     },
18267
18268     highlight : function(){
18269         var tree = this.node.getOwnerTree();
18270         Roo.fly(this.wrap).highlight(
18271             tree.hlColor || "C3DAF9",
18272             {endColor: tree.hlBaseColor}
18273         );
18274     },
18275
18276     collapse : function(){
18277         this.updateExpandIcon();
18278         this.ctNode.style.display = "none";
18279     },
18280
18281     animCollapse : function(callback){
18282         var ct = Roo.get(this.ctNode);
18283         ct.enableDisplayMode('block');
18284         ct.stopFx();
18285
18286         this.animating = true;
18287         this.updateExpandIcon();
18288
18289         ct.slideOut('t', {
18290             callback : function(){
18291                this.animating = false;
18292                Roo.callback(callback);
18293             },
18294             scope: this,
18295             duration: this.node.ownerTree.duration || .25
18296         });
18297     },
18298
18299     getContainer : function(){
18300         return this.ctNode;
18301     },
18302
18303     getEl : function(){
18304         return this.wrap;
18305     },
18306
18307     appendDDGhost : function(ghostNode){
18308         ghostNode.appendChild(this.elNode.cloneNode(true));
18309     },
18310
18311     getDDRepairXY : function(){
18312         return Roo.lib.Dom.getXY(this.iconNode);
18313     },
18314
18315     onRender : function(){
18316         this.render();
18317     },
18318
18319     render : function(bulkRender){
18320         var n = this.node, a = n.attributes;
18321         var targetNode = n.parentNode ?
18322               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
18323
18324         if(!this.rendered){
18325             this.rendered = true;
18326
18327             this.renderElements(n, a, targetNode, bulkRender);
18328
18329             if(a.qtip){
18330                if(this.textNode.setAttributeNS){
18331                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
18332                    if(a.qtipTitle){
18333                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
18334                    }
18335                }else{
18336                    this.textNode.setAttribute("ext:qtip", a.qtip);
18337                    if(a.qtipTitle){
18338                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
18339                    }
18340                }
18341             }else if(a.qtipCfg){
18342                 a.qtipCfg.target = Roo.id(this.textNode);
18343                 Roo.QuickTips.register(a.qtipCfg);
18344             }
18345             this.initEvents();
18346             if(!this.node.expanded){
18347                 this.updateExpandIcon();
18348             }
18349         }else{
18350             if(bulkRender === true) {
18351                 targetNode.appendChild(this.wrap);
18352             }
18353         }
18354     },
18355
18356     renderElements : function(n, a, targetNode, bulkRender)
18357     {
18358         // add some indent caching, this helps performance when rendering a large tree
18359         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18360         var t = n.getOwnerTree();
18361         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
18362         if (typeof(n.attributes.html) != 'undefined') {
18363             txt = n.attributes.html;
18364         }
18365         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
18366         var cb = typeof a.checked == 'boolean';
18367         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18368         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
18369             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
18370             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
18371             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
18372             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
18373             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
18374              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
18375                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
18376             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18377             "</li>"];
18378
18379         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18380             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18381                                 n.nextSibling.ui.getEl(), buf.join(""));
18382         }else{
18383             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18384         }
18385
18386         this.elNode = this.wrap.childNodes[0];
18387         this.ctNode = this.wrap.childNodes[1];
18388         var cs = this.elNode.childNodes;
18389         this.indentNode = cs[0];
18390         this.ecNode = cs[1];
18391         this.iconNode = cs[2];
18392         var index = 3;
18393         if(cb){
18394             this.checkbox = cs[3];
18395             index++;
18396         }
18397         this.anchor = cs[index];
18398         this.textNode = cs[index].firstChild;
18399     },
18400
18401     getAnchor : function(){
18402         return this.anchor;
18403     },
18404
18405     getTextEl : function(){
18406         return this.textNode;
18407     },
18408
18409     getIconEl : function(){
18410         return this.iconNode;
18411     },
18412
18413     isChecked : function(){
18414         return this.checkbox ? this.checkbox.checked : false;
18415     },
18416
18417     updateExpandIcon : function(){
18418         if(this.rendered){
18419             var n = this.node, c1, c2;
18420             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
18421             var hasChild = n.hasChildNodes();
18422             if(hasChild){
18423                 if(n.expanded){
18424                     cls += "-minus";
18425                     c1 = "x-tree-node-collapsed";
18426                     c2 = "x-tree-node-expanded";
18427                 }else{
18428                     cls += "-plus";
18429                     c1 = "x-tree-node-expanded";
18430                     c2 = "x-tree-node-collapsed";
18431                 }
18432                 if(this.wasLeaf){
18433                     this.removeClass("x-tree-node-leaf");
18434                     this.wasLeaf = false;
18435                 }
18436                 if(this.c1 != c1 || this.c2 != c2){
18437                     Roo.fly(this.elNode).replaceClass(c1, c2);
18438                     this.c1 = c1; this.c2 = c2;
18439                 }
18440             }else{
18441                 // this changes non-leafs into leafs if they have no children.
18442                 // it's not very rational behaviour..
18443                 
18444                 if(!this.wasLeaf && this.node.leaf){
18445                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
18446                     delete this.c1;
18447                     delete this.c2;
18448                     this.wasLeaf = true;
18449                 }
18450             }
18451             var ecc = "x-tree-ec-icon "+cls;
18452             if(this.ecc != ecc){
18453                 this.ecNode.className = ecc;
18454                 this.ecc = ecc;
18455             }
18456         }
18457     },
18458
18459     getChildIndent : function(){
18460         if(!this.childIndent){
18461             var buf = [];
18462             var p = this.node;
18463             while(p){
18464                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
18465                     if(!p.isLast()) {
18466                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
18467                     } else {
18468                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
18469                     }
18470                 }
18471                 p = p.parentNode;
18472             }
18473             this.childIndent = buf.join("");
18474         }
18475         return this.childIndent;
18476     },
18477
18478     renderIndent : function(){
18479         if(this.rendered){
18480             var indent = "";
18481             var p = this.node.parentNode;
18482             if(p){
18483                 indent = p.ui.getChildIndent();
18484             }
18485             if(this.indentMarkup != indent){ // don't rerender if not required
18486                 this.indentNode.innerHTML = indent;
18487                 this.indentMarkup = indent;
18488             }
18489             this.updateExpandIcon();
18490         }
18491     }
18492 };
18493
18494 Roo.tree.RootTreeNodeUI = function(){
18495     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
18496 };
18497 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
18498     render : function(){
18499         if(!this.rendered){
18500             var targetNode = this.node.ownerTree.innerCt.dom;
18501             this.node.expanded = true;
18502             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
18503             this.wrap = this.ctNode = targetNode.firstChild;
18504         }
18505     },
18506     collapse : function(){
18507     },
18508     expand : function(){
18509     }
18510 });/*
18511  * Based on:
18512  * Ext JS Library 1.1.1
18513  * Copyright(c) 2006-2007, Ext JS, LLC.
18514  *
18515  * Originally Released Under LGPL - original licence link has changed is not relivant.
18516  *
18517  * Fork - LGPL
18518  * <script type="text/javascript">
18519  */
18520 /**
18521  * @class Roo.tree.TreeLoader
18522  * @extends Roo.util.Observable
18523  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
18524  * nodes from a specified URL. The response must be a javascript Array definition
18525  * who's elements are node definition objects. eg:
18526  * <pre><code>
18527 {  success : true,
18528    data :      [
18529    
18530     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
18531     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
18532     ]
18533 }
18534
18535
18536 </code></pre>
18537  * <br><br>
18538  * The old style respose with just an array is still supported, but not recommended.
18539  * <br><br>
18540  *
18541  * A server request is sent, and child nodes are loaded only when a node is expanded.
18542  * The loading node's id is passed to the server under the parameter name "node" to
18543  * enable the server to produce the correct child nodes.
18544  * <br><br>
18545  * To pass extra parameters, an event handler may be attached to the "beforeload"
18546  * event, and the parameters specified in the TreeLoader's baseParams property:
18547  * <pre><code>
18548     myTreeLoader.on("beforeload", function(treeLoader, node) {
18549         this.baseParams.category = node.attributes.category;
18550     }, this);
18551 </code></pre><
18552  * This would pass an HTTP parameter called "category" to the server containing
18553  * the value of the Node's "category" attribute.
18554  * @constructor
18555  * Creates a new Treeloader.
18556  * @param {Object} config A config object containing config properties.
18557  */
18558 Roo.tree.TreeLoader = function(config){
18559     this.baseParams = {};
18560     this.requestMethod = "POST";
18561     Roo.apply(this, config);
18562
18563     this.addEvents({
18564     
18565         /**
18566          * @event beforeload
18567          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
18568          * @param {Object} This TreeLoader object.
18569          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18570          * @param {Object} callback The callback function specified in the {@link #load} call.
18571          */
18572         beforeload : true,
18573         /**
18574          * @event load
18575          * Fires when the node has been successfuly loaded.
18576          * @param {Object} This TreeLoader object.
18577          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18578          * @param {Object} response The response object containing the data from the server.
18579          */
18580         load : true,
18581         /**
18582          * @event loadexception
18583          * Fires if the network request failed.
18584          * @param {Object} This TreeLoader object.
18585          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18586          * @param {Object} response The response object containing the data from the server.
18587          */
18588         loadexception : true,
18589         /**
18590          * @event create
18591          * Fires before a node is created, enabling you to return custom Node types 
18592          * @param {Object} This TreeLoader object.
18593          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
18594          */
18595         create : true
18596     });
18597
18598     Roo.tree.TreeLoader.superclass.constructor.call(this);
18599 };
18600
18601 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
18602     /**
18603     * @cfg {String} dataUrl The URL from which to request a Json string which
18604     * specifies an array of node definition object representing the child nodes
18605     * to be loaded.
18606     */
18607     /**
18608     * @cfg {String} requestMethod either GET or POST
18609     * defaults to POST (due to BC)
18610     * to be loaded.
18611     */
18612     /**
18613     * @cfg {Object} baseParams (optional) An object containing properties which
18614     * specify HTTP parameters to be passed to each request for child nodes.
18615     */
18616     /**
18617     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
18618     * created by this loader. If the attributes sent by the server have an attribute in this object,
18619     * they take priority.
18620     */
18621     /**
18622     * @cfg {Object} uiProviders (optional) An object containing properties which
18623     * 
18624     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
18625     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
18626     * <i>uiProvider</i> attribute of a returned child node is a string rather
18627     * than a reference to a TreeNodeUI implementation, this that string value
18628     * is used as a property name in the uiProviders object. You can define the provider named
18629     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
18630     */
18631     uiProviders : {},
18632
18633     /**
18634     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
18635     * child nodes before loading.
18636     */
18637     clearOnLoad : true,
18638
18639     /**
18640     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
18641     * property on loading, rather than expecting an array. (eg. more compatible to a standard
18642     * Grid query { data : [ .....] }
18643     */
18644     
18645     root : false,
18646      /**
18647     * @cfg {String} queryParam (optional) 
18648     * Name of the query as it will be passed on the querystring (defaults to 'node')
18649     * eg. the request will be ?node=[id]
18650     */
18651     
18652     
18653     queryParam: false,
18654     
18655     /**
18656      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
18657      * This is called automatically when a node is expanded, but may be used to reload
18658      * a node (or append new children if the {@link #clearOnLoad} option is false.)
18659      * @param {Roo.tree.TreeNode} node
18660      * @param {Function} callback
18661      */
18662     load : function(node, callback){
18663         if(this.clearOnLoad){
18664             while(node.firstChild){
18665                 node.removeChild(node.firstChild);
18666             }
18667         }
18668         if(node.attributes.children){ // preloaded json children
18669             var cs = node.attributes.children;
18670             for(var i = 0, len = cs.length; i < len; i++){
18671                 node.appendChild(this.createNode(cs[i]));
18672             }
18673             if(typeof callback == "function"){
18674                 callback();
18675             }
18676         }else if(this.dataUrl){
18677             this.requestData(node, callback);
18678         }
18679     },
18680
18681     getParams: function(node){
18682         var buf = [], bp = this.baseParams;
18683         for(var key in bp){
18684             if(typeof bp[key] != "function"){
18685                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18686             }
18687         }
18688         var n = this.queryParam === false ? 'node' : this.queryParam;
18689         buf.push(n + "=", encodeURIComponent(node.id));
18690         return buf.join("");
18691     },
18692
18693     requestData : function(node, callback){
18694         if(this.fireEvent("beforeload", this, node, callback) !== false){
18695             this.transId = Roo.Ajax.request({
18696                 method:this.requestMethod,
18697                 url: this.dataUrl||this.url,
18698                 success: this.handleResponse,
18699                 failure: this.handleFailure,
18700                 scope: this,
18701                 argument: {callback: callback, node: node},
18702                 params: this.getParams(node)
18703             });
18704         }else{
18705             // if the load is cancelled, make sure we notify
18706             // the node that we are done
18707             if(typeof callback == "function"){
18708                 callback();
18709             }
18710         }
18711     },
18712
18713     isLoading : function(){
18714         return this.transId ? true : false;
18715     },
18716
18717     abort : function(){
18718         if(this.isLoading()){
18719             Roo.Ajax.abort(this.transId);
18720         }
18721     },
18722
18723     // private
18724     createNode : function(attr)
18725     {
18726         // apply baseAttrs, nice idea Corey!
18727         if(this.baseAttrs){
18728             Roo.applyIf(attr, this.baseAttrs);
18729         }
18730         if(this.applyLoader !== false){
18731             attr.loader = this;
18732         }
18733         // uiProvider = depreciated..
18734         
18735         if(typeof(attr.uiProvider) == 'string'){
18736            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
18737                 /**  eval:var:attr */ eval(attr.uiProvider);
18738         }
18739         if(typeof(this.uiProviders['default']) != 'undefined') {
18740             attr.uiProvider = this.uiProviders['default'];
18741         }
18742         
18743         this.fireEvent('create', this, attr);
18744         
18745         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18746         return(attr.leaf ?
18747                         new Roo.tree.TreeNode(attr) :
18748                         new Roo.tree.AsyncTreeNode(attr));
18749     },
18750
18751     processResponse : function(response, node, callback)
18752     {
18753         var json = response.responseText;
18754         try {
18755             
18756             var o = Roo.decode(json);
18757             
18758             if (this.root === false && typeof(o.success) != undefined) {
18759                 this.root = 'data'; // the default behaviour for list like data..
18760                 }
18761                 
18762             if (this.root !== false &&  !o.success) {
18763                 // it's a failure condition.
18764                 var a = response.argument;
18765                 this.fireEvent("loadexception", this, a.node, response);
18766                 Roo.log("Load failed - should have a handler really");
18767                 return;
18768             }
18769             
18770             
18771             
18772             if (this.root !== false) {
18773                  o = o[this.root];
18774             }
18775             
18776             for(var i = 0, len = o.length; i < len; i++){
18777                 var n = this.createNode(o[i]);
18778                 if(n){
18779                     node.appendChild(n);
18780                 }
18781             }
18782             if(typeof callback == "function"){
18783                 callback(this, node);
18784             }
18785         }catch(e){
18786             this.handleFailure(response);
18787         }
18788     },
18789
18790     handleResponse : function(response){
18791         this.transId = false;
18792         var a = response.argument;
18793         this.processResponse(response, a.node, a.callback);
18794         this.fireEvent("load", this, a.node, response);
18795     },
18796
18797     handleFailure : function(response)
18798     {
18799         // should handle failure better..
18800         this.transId = false;
18801         var a = response.argument;
18802         this.fireEvent("loadexception", this, a.node, response);
18803         if(typeof a.callback == "function"){
18804             a.callback(this, a.node);
18805         }
18806     }
18807 });/*
18808  * Based on:
18809  * Ext JS Library 1.1.1
18810  * Copyright(c) 2006-2007, Ext JS, LLC.
18811  *
18812  * Originally Released Under LGPL - original licence link has changed is not relivant.
18813  *
18814  * Fork - LGPL
18815  * <script type="text/javascript">
18816  */
18817
18818 /**
18819 * @class Roo.tree.TreeFilter
18820 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18821 * @param {TreePanel} tree
18822 * @param {Object} config (optional)
18823  */
18824 Roo.tree.TreeFilter = function(tree, config){
18825     this.tree = tree;
18826     this.filtered = {};
18827     Roo.apply(this, config);
18828 };
18829
18830 Roo.tree.TreeFilter.prototype = {
18831     clearBlank:false,
18832     reverse:false,
18833     autoClear:false,
18834     remove:false,
18835
18836      /**
18837      * Filter the data by a specific attribute.
18838      * @param {String/RegExp} value Either string that the attribute value
18839      * should start with or a RegExp to test against the attribute
18840      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18841      * @param {TreeNode} startNode (optional) The node to start the filter at.
18842      */
18843     filter : function(value, attr, startNode){
18844         attr = attr || "text";
18845         var f;
18846         if(typeof value == "string"){
18847             var vlen = value.length;
18848             // auto clear empty filter
18849             if(vlen == 0 && this.clearBlank){
18850                 this.clear();
18851                 return;
18852             }
18853             value = value.toLowerCase();
18854             f = function(n){
18855                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18856             };
18857         }else if(value.exec){ // regex?
18858             f = function(n){
18859                 return value.test(n.attributes[attr]);
18860             };
18861         }else{
18862             throw 'Illegal filter type, must be string or regex';
18863         }
18864         this.filterBy(f, null, startNode);
18865         },
18866
18867     /**
18868      * Filter by a function. The passed function will be called with each
18869      * node in the tree (or from the startNode). If the function returns true, the node is kept
18870      * otherwise it is filtered. If a node is filtered, its children are also filtered.
18871      * @param {Function} fn The filter function
18872      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18873      */
18874     filterBy : function(fn, scope, startNode){
18875         startNode = startNode || this.tree.root;
18876         if(this.autoClear){
18877             this.clear();
18878         }
18879         var af = this.filtered, rv = this.reverse;
18880         var f = function(n){
18881             if(n == startNode){
18882                 return true;
18883             }
18884             if(af[n.id]){
18885                 return false;
18886             }
18887             var m = fn.call(scope || n, n);
18888             if(!m || rv){
18889                 af[n.id] = n;
18890                 n.ui.hide();
18891                 return false;
18892             }
18893             return true;
18894         };
18895         startNode.cascade(f);
18896         if(this.remove){
18897            for(var id in af){
18898                if(typeof id != "function"){
18899                    var n = af[id];
18900                    if(n && n.parentNode){
18901                        n.parentNode.removeChild(n);
18902                    }
18903                }
18904            }
18905         }
18906     },
18907
18908     /**
18909      * Clears the current filter. Note: with the "remove" option
18910      * set a filter cannot be cleared.
18911      */
18912     clear : function(){
18913         var t = this.tree;
18914         var af = this.filtered;
18915         for(var id in af){
18916             if(typeof id != "function"){
18917                 var n = af[id];
18918                 if(n){
18919                     n.ui.show();
18920                 }
18921             }
18922         }
18923         this.filtered = {};
18924     }
18925 };
18926 /*
18927  * Based on:
18928  * Ext JS Library 1.1.1
18929  * Copyright(c) 2006-2007, Ext JS, LLC.
18930  *
18931  * Originally Released Under LGPL - original licence link has changed is not relivant.
18932  *
18933  * Fork - LGPL
18934  * <script type="text/javascript">
18935  */
18936  
18937
18938 /**
18939  * @class Roo.tree.TreeSorter
18940  * Provides sorting of nodes in a TreePanel
18941  * 
18942  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18943  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18944  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18945  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18946  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18947  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18948  * @constructor
18949  * @param {TreePanel} tree
18950  * @param {Object} config
18951  */
18952 Roo.tree.TreeSorter = function(tree, config){
18953     Roo.apply(this, config);
18954     tree.on("beforechildrenrendered", this.doSort, this);
18955     tree.on("append", this.updateSort, this);
18956     tree.on("insert", this.updateSort, this);
18957     
18958     var dsc = this.dir && this.dir.toLowerCase() == "desc";
18959     var p = this.property || "text";
18960     var sortType = this.sortType;
18961     var fs = this.folderSort;
18962     var cs = this.caseSensitive === true;
18963     var leafAttr = this.leafAttr || 'leaf';
18964
18965     this.sortFn = function(n1, n2){
18966         if(fs){
18967             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18968                 return 1;
18969             }
18970             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
18971                 return -1;
18972             }
18973         }
18974         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
18975         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
18976         if(v1 < v2){
18977                         return dsc ? +1 : -1;
18978                 }else if(v1 > v2){
18979                         return dsc ? -1 : +1;
18980         }else{
18981                 return 0;
18982         }
18983     };
18984 };
18985
18986 Roo.tree.TreeSorter.prototype = {
18987     doSort : function(node){
18988         node.sort(this.sortFn);
18989     },
18990     
18991     compareNodes : function(n1, n2){
18992         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
18993     },
18994     
18995     updateSort : function(tree, node){
18996         if(node.childrenRendered){
18997             this.doSort.defer(1, this, [node]);
18998         }
18999     }
19000 };/*
19001  * Based on:
19002  * Ext JS Library 1.1.1
19003  * Copyright(c) 2006-2007, Ext JS, LLC.
19004  *
19005  * Originally Released Under LGPL - original licence link has changed is not relivant.
19006  *
19007  * Fork - LGPL
19008  * <script type="text/javascript">
19009  */
19010
19011 if(Roo.dd.DropZone){
19012     
19013 Roo.tree.TreeDropZone = function(tree, config){
19014     this.allowParentInsert = false;
19015     this.allowContainerDrop = false;
19016     this.appendOnly = false;
19017     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
19018     this.tree = tree;
19019     this.lastInsertClass = "x-tree-no-status";
19020     this.dragOverData = {};
19021 };
19022
19023 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
19024     ddGroup : "TreeDD",
19025     scroll:  true,
19026     
19027     expandDelay : 1000,
19028     
19029     expandNode : function(node){
19030         if(node.hasChildNodes() && !node.isExpanded()){
19031             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
19032         }
19033     },
19034     
19035     queueExpand : function(node){
19036         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
19037     },
19038     
19039     cancelExpand : function(){
19040         if(this.expandProcId){
19041             clearTimeout(this.expandProcId);
19042             this.expandProcId = false;
19043         }
19044     },
19045     
19046     isValidDropPoint : function(n, pt, dd, e, data){
19047         if(!n || !data){ return false; }
19048         var targetNode = n.node;
19049         var dropNode = data.node;
19050         // default drop rules
19051         if(!(targetNode && targetNode.isTarget && pt)){
19052             return false;
19053         }
19054         if(pt == "append" && targetNode.allowChildren === false){
19055             return false;
19056         }
19057         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
19058             return false;
19059         }
19060         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
19061             return false;
19062         }
19063         // reuse the object
19064         var overEvent = this.dragOverData;
19065         overEvent.tree = this.tree;
19066         overEvent.target = targetNode;
19067         overEvent.data = data;
19068         overEvent.point = pt;
19069         overEvent.source = dd;
19070         overEvent.rawEvent = e;
19071         overEvent.dropNode = dropNode;
19072         overEvent.cancel = false;  
19073         var result = this.tree.fireEvent("nodedragover", overEvent);
19074         return overEvent.cancel === false && result !== false;
19075     },
19076     
19077     getDropPoint : function(e, n, dd)
19078     {
19079         var tn = n.node;
19080         if(tn.isRoot){
19081             return tn.allowChildren !== false ? "append" : false; // always append for root
19082         }
19083         var dragEl = n.ddel;
19084         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
19085         var y = Roo.lib.Event.getPageY(e);
19086         //var noAppend = tn.allowChildren === false || tn.isLeaf();
19087         
19088         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
19089         var noAppend = tn.allowChildren === false;
19090         if(this.appendOnly || tn.parentNode.allowChildren === false){
19091             return noAppend ? false : "append";
19092         }
19093         var noBelow = false;
19094         if(!this.allowParentInsert){
19095             noBelow = tn.hasChildNodes() && tn.isExpanded();
19096         }
19097         var q = (b - t) / (noAppend ? 2 : 3);
19098         if(y >= t && y < (t + q)){
19099             return "above";
19100         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
19101             return "below";
19102         }else{
19103             return "append";
19104         }
19105     },
19106     
19107     onNodeEnter : function(n, dd, e, data)
19108     {
19109         this.cancelExpand();
19110     },
19111     
19112     onNodeOver : function(n, dd, e, data)
19113     {
19114        
19115         var pt = this.getDropPoint(e, n, dd);
19116         var node = n.node;
19117         
19118         // auto node expand check
19119         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
19120             this.queueExpand(node);
19121         }else if(pt != "append"){
19122             this.cancelExpand();
19123         }
19124         
19125         // set the insert point style on the target node
19126         var returnCls = this.dropNotAllowed;
19127         if(this.isValidDropPoint(n, pt, dd, e, data)){
19128            if(pt){
19129                var el = n.ddel;
19130                var cls;
19131                if(pt == "above"){
19132                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
19133                    cls = "x-tree-drag-insert-above";
19134                }else if(pt == "below"){
19135                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
19136                    cls = "x-tree-drag-insert-below";
19137                }else{
19138                    returnCls = "x-tree-drop-ok-append";
19139                    cls = "x-tree-drag-append";
19140                }
19141                if(this.lastInsertClass != cls){
19142                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
19143                    this.lastInsertClass = cls;
19144                }
19145            }
19146        }
19147        return returnCls;
19148     },
19149     
19150     onNodeOut : function(n, dd, e, data){
19151         
19152         this.cancelExpand();
19153         this.removeDropIndicators(n);
19154     },
19155     
19156     onNodeDrop : function(n, dd, e, data){
19157         var point = this.getDropPoint(e, n, dd);
19158         var targetNode = n.node;
19159         targetNode.ui.startDrop();
19160         if(!this.isValidDropPoint(n, point, dd, e, data)){
19161             targetNode.ui.endDrop();
19162             return false;
19163         }
19164         // first try to find the drop node
19165         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
19166         var dropEvent = {
19167             tree : this.tree,
19168             target: targetNode,
19169             data: data,
19170             point: point,
19171             source: dd,
19172             rawEvent: e,
19173             dropNode: dropNode,
19174             cancel: !dropNode   
19175         };
19176         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
19177         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
19178             targetNode.ui.endDrop();
19179             return false;
19180         }
19181         // allow target changing
19182         targetNode = dropEvent.target;
19183         if(point == "append" && !targetNode.isExpanded()){
19184             targetNode.expand(false, null, function(){
19185                 this.completeDrop(dropEvent);
19186             }.createDelegate(this));
19187         }else{
19188             this.completeDrop(dropEvent);
19189         }
19190         return true;
19191     },
19192     
19193     completeDrop : function(de){
19194         var ns = de.dropNode, p = de.point, t = de.target;
19195         if(!(ns instanceof Array)){
19196             ns = [ns];
19197         }
19198         var n;
19199         for(var i = 0, len = ns.length; i < len; i++){
19200             n = ns[i];
19201             if(p == "above"){
19202                 t.parentNode.insertBefore(n, t);
19203             }else if(p == "below"){
19204                 t.parentNode.insertBefore(n, t.nextSibling);
19205             }else{
19206                 t.appendChild(n);
19207             }
19208         }
19209         n.ui.focus();
19210         if(this.tree.hlDrop){
19211             n.ui.highlight();
19212         }
19213         t.ui.endDrop();
19214         this.tree.fireEvent("nodedrop", de);
19215     },
19216     
19217     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
19218         if(this.tree.hlDrop){
19219             dropNode.ui.focus();
19220             dropNode.ui.highlight();
19221         }
19222         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
19223     },
19224     
19225     getTree : function(){
19226         return this.tree;
19227     },
19228     
19229     removeDropIndicators : function(n){
19230         if(n && n.ddel){
19231             var el = n.ddel;
19232             Roo.fly(el).removeClass([
19233                     "x-tree-drag-insert-above",
19234                     "x-tree-drag-insert-below",
19235                     "x-tree-drag-append"]);
19236             this.lastInsertClass = "_noclass";
19237         }
19238     },
19239     
19240     beforeDragDrop : function(target, e, id){
19241         this.cancelExpand();
19242         return true;
19243     },
19244     
19245     afterRepair : function(data){
19246         if(data && Roo.enableFx){
19247             data.node.ui.highlight();
19248         }
19249         this.hideProxy();
19250     } 
19251     
19252 });
19253
19254 }
19255 /*
19256  * Based on:
19257  * Ext JS Library 1.1.1
19258  * Copyright(c) 2006-2007, Ext JS, LLC.
19259  *
19260  * Originally Released Under LGPL - original licence link has changed is not relivant.
19261  *
19262  * Fork - LGPL
19263  * <script type="text/javascript">
19264  */
19265  
19266
19267 if(Roo.dd.DragZone){
19268 Roo.tree.TreeDragZone = function(tree, config){
19269     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
19270     this.tree = tree;
19271 };
19272
19273 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
19274     ddGroup : "TreeDD",
19275    
19276     onBeforeDrag : function(data, e){
19277         var n = data.node;
19278         return n && n.draggable && !n.disabled;
19279     },
19280      
19281     
19282     onInitDrag : function(e){
19283         var data = this.dragData;
19284         this.tree.getSelectionModel().select(data.node);
19285         this.proxy.update("");
19286         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
19287         this.tree.fireEvent("startdrag", this.tree, data.node, e);
19288     },
19289     
19290     getRepairXY : function(e, data){
19291         return data.node.ui.getDDRepairXY();
19292     },
19293     
19294     onEndDrag : function(data, e){
19295         this.tree.fireEvent("enddrag", this.tree, data.node, e);
19296         
19297         
19298     },
19299     
19300     onValidDrop : function(dd, e, id){
19301         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
19302         this.hideProxy();
19303     },
19304     
19305     beforeInvalidDrop : function(e, id){
19306         // this scrolls the original position back into view
19307         var sm = this.tree.getSelectionModel();
19308         sm.clearSelections();
19309         sm.select(this.dragData.node);
19310     }
19311 });
19312 }/*
19313  * Based on:
19314  * Ext JS Library 1.1.1
19315  * Copyright(c) 2006-2007, Ext JS, LLC.
19316  *
19317  * Originally Released Under LGPL - original licence link has changed is not relivant.
19318  *
19319  * Fork - LGPL
19320  * <script type="text/javascript">
19321  */
19322 /**
19323  * @class Roo.tree.TreeEditor
19324  * @extends Roo.Editor
19325  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
19326  * as the editor field.
19327  * @constructor
19328  * @param {Object} config (used to be the tree panel.)
19329  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
19330  * 
19331  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
19332  * @cfg {Roo.form.TextField|Object} field The field configuration
19333  *
19334  * 
19335  */
19336 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
19337     var tree = config;
19338     var field;
19339     if (oldconfig) { // old style..
19340         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
19341     } else {
19342         // new style..
19343         tree = config.tree;
19344         config.field = config.field  || {};
19345         config.field.xtype = 'TextField';
19346         field = Roo.factory(config.field, Roo.form);
19347     }
19348     config = config || {};
19349     
19350     
19351     this.addEvents({
19352         /**
19353          * @event beforenodeedit
19354          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
19355          * false from the handler of this event.
19356          * @param {Editor} this
19357          * @param {Roo.tree.Node} node 
19358          */
19359         "beforenodeedit" : true
19360     });
19361     
19362     //Roo.log(config);
19363     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
19364
19365     this.tree = tree;
19366
19367     tree.on('beforeclick', this.beforeNodeClick, this);
19368     tree.getTreeEl().on('mousedown', this.hide, this);
19369     this.on('complete', this.updateNode, this);
19370     this.on('beforestartedit', this.fitToTree, this);
19371     this.on('startedit', this.bindScroll, this, {delay:10});
19372     this.on('specialkey', this.onSpecialKey, this);
19373 };
19374
19375 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
19376     /**
19377      * @cfg {String} alignment
19378      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
19379      */
19380     alignment: "l-l",
19381     // inherit
19382     autoSize: false,
19383     /**
19384      * @cfg {Boolean} hideEl
19385      * True to hide the bound element while the editor is displayed (defaults to false)
19386      */
19387     hideEl : false,
19388     /**
19389      * @cfg {String} cls
19390      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
19391      */
19392     cls: "x-small-editor x-tree-editor",
19393     /**
19394      * @cfg {Boolean} shim
19395      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
19396      */
19397     shim:false,
19398     // inherit
19399     shadow:"frame",
19400     /**
19401      * @cfg {Number} maxWidth
19402      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
19403      * the containing tree element's size, it will be automatically limited for you to the container width, taking
19404      * scroll and client offsets into account prior to each edit.
19405      */
19406     maxWidth: 250,
19407
19408     editDelay : 350,
19409
19410     // private
19411     fitToTree : function(ed, el){
19412         var td = this.tree.getTreeEl().dom, nd = el.dom;
19413         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
19414             td.scrollLeft = nd.offsetLeft;
19415         }
19416         var w = Math.min(
19417                 this.maxWidth,
19418                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
19419         this.setSize(w, '');
19420         
19421         return this.fireEvent('beforenodeedit', this, this.editNode);
19422         
19423     },
19424
19425     // private
19426     triggerEdit : function(node){
19427         this.completeEdit();
19428         this.editNode = node;
19429         this.startEdit(node.ui.textNode, node.text);
19430     },
19431
19432     // private
19433     bindScroll : function(){
19434         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
19435     },
19436
19437     // private
19438     beforeNodeClick : function(node, e){
19439         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
19440         this.lastClick = new Date();
19441         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
19442             e.stopEvent();
19443             this.triggerEdit(node);
19444             return false;
19445         }
19446         return true;
19447     },
19448
19449     // private
19450     updateNode : function(ed, value){
19451         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
19452         this.editNode.setText(value);
19453     },
19454
19455     // private
19456     onHide : function(){
19457         Roo.tree.TreeEditor.superclass.onHide.call(this);
19458         if(this.editNode){
19459             this.editNode.ui.focus();
19460         }
19461     },
19462
19463     // private
19464     onSpecialKey : function(field, e){
19465         var k = e.getKey();
19466         if(k == e.ESC){
19467             e.stopEvent();
19468             this.cancelEdit();
19469         }else if(k == e.ENTER && !e.hasModifier()){
19470             e.stopEvent();
19471             this.completeEdit();
19472         }
19473     }
19474 });//<Script type="text/javascript">
19475 /*
19476  * Based on:
19477  * Ext JS Library 1.1.1
19478  * Copyright(c) 2006-2007, Ext JS, LLC.
19479  *
19480  * Originally Released Under LGPL - original licence link has changed is not relivant.
19481  *
19482  * Fork - LGPL
19483  * <script type="text/javascript">
19484  */
19485  
19486 /**
19487  * Not documented??? - probably should be...
19488  */
19489
19490 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
19491     //focus: Roo.emptyFn, // prevent odd scrolling behavior
19492     
19493     renderElements : function(n, a, targetNode, bulkRender){
19494         //consel.log("renderElements?");
19495         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
19496
19497         var t = n.getOwnerTree();
19498         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
19499         
19500         var cols = t.columns;
19501         var bw = t.borderWidth;
19502         var c = cols[0];
19503         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
19504          var cb = typeof a.checked == "boolean";
19505         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19506         var colcls = 'x-t-' + tid + '-c0';
19507         var buf = [
19508             '<li class="x-tree-node">',
19509             
19510                 
19511                 '<div class="x-tree-node-el ', a.cls,'">',
19512                     // extran...
19513                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
19514                 
19515                 
19516                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
19517                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
19518                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
19519                            (a.icon ? ' x-tree-node-inline-icon' : ''),
19520                            (a.iconCls ? ' '+a.iconCls : ''),
19521                            '" unselectable="on" />',
19522                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
19523                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
19524                              
19525                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19526                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
19527                             '<span unselectable="on" qtip="' + tx + '">',
19528                              tx,
19529                              '</span></a>' ,
19530                     '</div>',
19531                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19532                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
19533                  ];
19534         for(var i = 1, len = cols.length; i < len; i++){
19535             c = cols[i];
19536             colcls = 'x-t-' + tid + '-c' +i;
19537             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19538             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
19539                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
19540                       "</div>");
19541          }
19542          
19543          buf.push(
19544             '</a>',
19545             '<div class="x-clear"></div></div>',
19546             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
19547             "</li>");
19548         
19549         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
19550             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
19551                                 n.nextSibling.ui.getEl(), buf.join(""));
19552         }else{
19553             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
19554         }
19555         var el = this.wrap.firstChild;
19556         this.elRow = el;
19557         this.elNode = el.firstChild;
19558         this.ranchor = el.childNodes[1];
19559         this.ctNode = this.wrap.childNodes[1];
19560         var cs = el.firstChild.childNodes;
19561         this.indentNode = cs[0];
19562         this.ecNode = cs[1];
19563         this.iconNode = cs[2];
19564         var index = 3;
19565         if(cb){
19566             this.checkbox = cs[3];
19567             index++;
19568         }
19569         this.anchor = cs[index];
19570         
19571         this.textNode = cs[index].firstChild;
19572         
19573         //el.on("click", this.onClick, this);
19574         //el.on("dblclick", this.onDblClick, this);
19575         
19576         
19577        // console.log(this);
19578     },
19579     initEvents : function(){
19580         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
19581         
19582             
19583         var a = this.ranchor;
19584
19585         var el = Roo.get(a);
19586
19587         if(Roo.isOpera){ // opera render bug ignores the CSS
19588             el.setStyle("text-decoration", "none");
19589         }
19590
19591         el.on("click", this.onClick, this);
19592         el.on("dblclick", this.onDblClick, this);
19593         el.on("contextmenu", this.onContextMenu, this);
19594         
19595     },
19596     
19597     /*onSelectedChange : function(state){
19598         if(state){
19599             this.focus();
19600             this.addClass("x-tree-selected");
19601         }else{
19602             //this.blur();
19603             this.removeClass("x-tree-selected");
19604         }
19605     },*/
19606     addClass : function(cls){
19607         if(this.elRow){
19608             Roo.fly(this.elRow).addClass(cls);
19609         }
19610         
19611     },
19612     
19613     
19614     removeClass : function(cls){
19615         if(this.elRow){
19616             Roo.fly(this.elRow).removeClass(cls);
19617         }
19618     }
19619
19620     
19621     
19622 });//<Script type="text/javascript">
19623
19624 /*
19625  * Based on:
19626  * Ext JS Library 1.1.1
19627  * Copyright(c) 2006-2007, Ext JS, LLC.
19628  *
19629  * Originally Released Under LGPL - original licence link has changed is not relivant.
19630  *
19631  * Fork - LGPL
19632  * <script type="text/javascript">
19633  */
19634  
19635
19636 /**
19637  * @class Roo.tree.ColumnTree
19638  * @extends Roo.data.TreePanel
19639  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
19640  * @cfg {int} borderWidth  compined right/left border allowance
19641  * @constructor
19642  * @param {String/HTMLElement/Element} el The container element
19643  * @param {Object} config
19644  */
19645 Roo.tree.ColumnTree =  function(el, config)
19646 {
19647    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
19648    this.addEvents({
19649         /**
19650         * @event resize
19651         * Fire this event on a container when it resizes
19652         * @param {int} w Width
19653         * @param {int} h Height
19654         */
19655        "resize" : true
19656     });
19657     this.on('resize', this.onResize, this);
19658 };
19659
19660 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
19661     //lines:false,
19662     
19663     
19664     borderWidth: Roo.isBorderBox ? 0 : 2, 
19665     headEls : false,
19666     
19667     render : function(){
19668         // add the header.....
19669        
19670         Roo.tree.ColumnTree.superclass.render.apply(this);
19671         
19672         this.el.addClass('x-column-tree');
19673         
19674         this.headers = this.el.createChild(
19675             {cls:'x-tree-headers'},this.innerCt.dom);
19676    
19677         var cols = this.columns, c;
19678         var totalWidth = 0;
19679         this.headEls = [];
19680         var  len = cols.length;
19681         for(var i = 0; i < len; i++){
19682              c = cols[i];
19683              totalWidth += c.width;
19684             this.headEls.push(this.headers.createChild({
19685                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19686                  cn: {
19687                      cls:'x-tree-hd-text',
19688                      html: c.header
19689                  },
19690                  style:'width:'+(c.width-this.borderWidth)+'px;'
19691              }));
19692         }
19693         this.headers.createChild({cls:'x-clear'});
19694         // prevent floats from wrapping when clipped
19695         this.headers.setWidth(totalWidth);
19696         //this.innerCt.setWidth(totalWidth);
19697         this.innerCt.setStyle({ overflow: 'auto' });
19698         this.onResize(this.width, this.height);
19699              
19700         
19701     },
19702     onResize : function(w,h)
19703     {
19704         this.height = h;
19705         this.width = w;
19706         // resize cols..
19707         this.innerCt.setWidth(this.width);
19708         this.innerCt.setHeight(this.height-20);
19709         
19710         // headers...
19711         var cols = this.columns, c;
19712         var totalWidth = 0;
19713         var expEl = false;
19714         var len = cols.length;
19715         for(var i = 0; i < len; i++){
19716             c = cols[i];
19717             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19718                 // it's the expander..
19719                 expEl  = this.headEls[i];
19720                 continue;
19721             }
19722             totalWidth += c.width;
19723             
19724         }
19725         if (expEl) {
19726             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
19727         }
19728         this.headers.setWidth(w-20);
19729
19730         
19731         
19732         
19733     }
19734 });
19735 /*
19736  * Based on:
19737  * Ext JS Library 1.1.1
19738  * Copyright(c) 2006-2007, Ext JS, LLC.
19739  *
19740  * Originally Released Under LGPL - original licence link has changed is not relivant.
19741  *
19742  * Fork - LGPL
19743  * <script type="text/javascript">
19744  */
19745  
19746 /**
19747  * @class Roo.menu.Menu
19748  * @extends Roo.util.Observable
19749  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
19750  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19751  * @constructor
19752  * Creates a new Menu
19753  * @param {Object} config Configuration options
19754  */
19755 Roo.menu.Menu = function(config){
19756     Roo.apply(this, config);
19757     this.id = this.id || Roo.id();
19758     this.addEvents({
19759         /**
19760          * @event beforeshow
19761          * Fires before this menu is displayed
19762          * @param {Roo.menu.Menu} this
19763          */
19764         beforeshow : true,
19765         /**
19766          * @event beforehide
19767          * Fires before this menu is hidden
19768          * @param {Roo.menu.Menu} this
19769          */
19770         beforehide : true,
19771         /**
19772          * @event show
19773          * Fires after this menu is displayed
19774          * @param {Roo.menu.Menu} this
19775          */
19776         show : true,
19777         /**
19778          * @event hide
19779          * Fires after this menu is hidden
19780          * @param {Roo.menu.Menu} this
19781          */
19782         hide : true,
19783         /**
19784          * @event click
19785          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19786          * @param {Roo.menu.Menu} this
19787          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19788          * @param {Roo.EventObject} e
19789          */
19790         click : true,
19791         /**
19792          * @event mouseover
19793          * Fires when the mouse is hovering over this menu
19794          * @param {Roo.menu.Menu} this
19795          * @param {Roo.EventObject} e
19796          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19797          */
19798         mouseover : true,
19799         /**
19800          * @event mouseout
19801          * Fires when the mouse exits this menu
19802          * @param {Roo.menu.Menu} this
19803          * @param {Roo.EventObject} e
19804          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19805          */
19806         mouseout : true,
19807         /**
19808          * @event itemclick
19809          * Fires when a menu item contained in this menu is clicked
19810          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19811          * @param {Roo.EventObject} e
19812          */
19813         itemclick: true
19814     });
19815     if (this.registerMenu) {
19816         Roo.menu.MenuMgr.register(this);
19817     }
19818     
19819     var mis = this.items;
19820     this.items = new Roo.util.MixedCollection();
19821     if(mis){
19822         this.add.apply(this, mis);
19823     }
19824 };
19825
19826 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19827     /**
19828      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19829      */
19830     minWidth : 120,
19831     /**
19832      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19833      * for bottom-right shadow (defaults to "sides")
19834      */
19835     shadow : "sides",
19836     /**
19837      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19838      * this menu (defaults to "tl-tr?")
19839      */
19840     subMenuAlign : "tl-tr?",
19841     /**
19842      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19843      * relative to its element of origin (defaults to "tl-bl?")
19844      */
19845     defaultAlign : "tl-bl?",
19846     /**
19847      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19848      */
19849     allowOtherMenus : false,
19850     /**
19851      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19852      */
19853     registerMenu : true,
19854
19855     hidden:true,
19856
19857     // private
19858     render : function(){
19859         if(this.el){
19860             return;
19861         }
19862         var el = this.el = new Roo.Layer({
19863             cls: "x-menu",
19864             shadow:this.shadow,
19865             constrain: false,
19866             parentEl: this.parentEl || document.body,
19867             zindex:15000
19868         });
19869
19870         this.keyNav = new Roo.menu.MenuNav(this);
19871
19872         if(this.plain){
19873             el.addClass("x-menu-plain");
19874         }
19875         if(this.cls){
19876             el.addClass(this.cls);
19877         }
19878         // generic focus element
19879         this.focusEl = el.createChild({
19880             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19881         });
19882         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19883         ul.on("click", this.onClick, this);
19884         ul.on("mouseover", this.onMouseOver, this);
19885         ul.on("mouseout", this.onMouseOut, this);
19886         this.items.each(function(item){
19887             if (item.hidden) {
19888                 return;
19889             }
19890             
19891             var li = document.createElement("li");
19892             li.className = "x-menu-list-item";
19893             ul.dom.appendChild(li);
19894             item.render(li, this);
19895         }, this);
19896         this.ul = ul;
19897         this.autoWidth();
19898     },
19899
19900     // private
19901     autoWidth : function(){
19902         var el = this.el, ul = this.ul;
19903         if(!el){
19904             return;
19905         }
19906         var w = this.width;
19907         if(w){
19908             el.setWidth(w);
19909         }else if(Roo.isIE){
19910             el.setWidth(this.minWidth);
19911             var t = el.dom.offsetWidth; // force recalc
19912             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19913         }
19914     },
19915
19916     // private
19917     delayAutoWidth : function(){
19918         if(this.rendered){
19919             if(!this.awTask){
19920                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19921             }
19922             this.awTask.delay(20);
19923         }
19924     },
19925
19926     // private
19927     findTargetItem : function(e){
19928         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
19929         if(t && t.menuItemId){
19930             return this.items.get(t.menuItemId);
19931         }
19932     },
19933
19934     // private
19935     onClick : function(e){
19936         var t;
19937         if(t = this.findTargetItem(e)){
19938             t.onClick(e);
19939             this.fireEvent("click", this, t, e);
19940         }
19941     },
19942
19943     // private
19944     setActiveItem : function(item, autoExpand){
19945         if(item != this.activeItem){
19946             if(this.activeItem){
19947                 this.activeItem.deactivate();
19948             }
19949             this.activeItem = item;
19950             item.activate(autoExpand);
19951         }else if(autoExpand){
19952             item.expandMenu();
19953         }
19954     },
19955
19956     // private
19957     tryActivate : function(start, step){
19958         var items = this.items;
19959         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19960             var item = items.get(i);
19961             if(!item.disabled && item.canActivate){
19962                 this.setActiveItem(item, false);
19963                 return item;
19964             }
19965         }
19966         return false;
19967     },
19968
19969     // private
19970     onMouseOver : function(e){
19971         var t;
19972         if(t = this.findTargetItem(e)){
19973             if(t.canActivate && !t.disabled){
19974                 this.setActiveItem(t, true);
19975             }
19976         }
19977         this.fireEvent("mouseover", this, e, t);
19978     },
19979
19980     // private
19981     onMouseOut : function(e){
19982         var t;
19983         if(t = this.findTargetItem(e)){
19984             if(t == this.activeItem && t.shouldDeactivate(e)){
19985                 this.activeItem.deactivate();
19986                 delete this.activeItem;
19987             }
19988         }
19989         this.fireEvent("mouseout", this, e, t);
19990     },
19991
19992     /**
19993      * Read-only.  Returns true if the menu is currently displayed, else false.
19994      * @type Boolean
19995      */
19996     isVisible : function(){
19997         return this.el && !this.hidden;
19998     },
19999
20000     /**
20001      * Displays this menu relative to another element
20002      * @param {String/HTMLElement/Roo.Element} element The element to align to
20003      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
20004      * the element (defaults to this.defaultAlign)
20005      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
20006      */
20007     show : function(el, pos, parentMenu){
20008         this.parentMenu = parentMenu;
20009         if(!this.el){
20010             this.render();
20011         }
20012         this.fireEvent("beforeshow", this);
20013         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
20014     },
20015
20016     /**
20017      * Displays this menu at a specific xy position
20018      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
20019      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
20020      */
20021     showAt : function(xy, parentMenu, /* private: */_e){
20022         this.parentMenu = parentMenu;
20023         if(!this.el){
20024             this.render();
20025         }
20026         if(_e !== false){
20027             this.fireEvent("beforeshow", this);
20028             xy = this.el.adjustForConstraints(xy);
20029         }
20030         this.el.setXY(xy);
20031         this.el.show();
20032         this.hidden = false;
20033         this.focus();
20034         this.fireEvent("show", this);
20035     },
20036
20037     focus : function(){
20038         if(!this.hidden){
20039             this.doFocus.defer(50, this);
20040         }
20041     },
20042
20043     doFocus : function(){
20044         if(!this.hidden){
20045             this.focusEl.focus();
20046         }
20047     },
20048
20049     /**
20050      * Hides this menu and optionally all parent menus
20051      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
20052      */
20053     hide : function(deep){
20054         if(this.el && this.isVisible()){
20055             this.fireEvent("beforehide", this);
20056             if(this.activeItem){
20057                 this.activeItem.deactivate();
20058                 this.activeItem = null;
20059             }
20060             this.el.hide();
20061             this.hidden = true;
20062             this.fireEvent("hide", this);
20063         }
20064         if(deep === true && this.parentMenu){
20065             this.parentMenu.hide(true);
20066         }
20067     },
20068
20069     /**
20070      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
20071      * Any of the following are valid:
20072      * <ul>
20073      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
20074      * <li>An HTMLElement object which will be converted to a menu item</li>
20075      * <li>A menu item config object that will be created as a new menu item</li>
20076      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
20077      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
20078      * </ul>
20079      * Usage:
20080      * <pre><code>
20081 // Create the menu
20082 var menu = new Roo.menu.Menu();
20083
20084 // Create a menu item to add by reference
20085 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
20086
20087 // Add a bunch of items at once using different methods.
20088 // Only the last item added will be returned.
20089 var item = menu.add(
20090     menuItem,                // add existing item by ref
20091     'Dynamic Item',          // new TextItem
20092     '-',                     // new separator
20093     { text: 'Config Item' }  // new item by config
20094 );
20095 </code></pre>
20096      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
20097      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
20098      */
20099     add : function(){
20100         var a = arguments, l = a.length, item;
20101         for(var i = 0; i < l; i++){
20102             var el = a[i];
20103             if ((typeof(el) == "object") && el.xtype && el.xns) {
20104                 el = Roo.factory(el, Roo.menu);
20105             }
20106             
20107             if(el.render){ // some kind of Item
20108                 item = this.addItem(el);
20109             }else if(typeof el == "string"){ // string
20110                 if(el == "separator" || el == "-"){
20111                     item = this.addSeparator();
20112                 }else{
20113                     item = this.addText(el);
20114                 }
20115             }else if(el.tagName || el.el){ // element
20116                 item = this.addElement(el);
20117             }else if(typeof el == "object"){ // must be menu item config?
20118                 item = this.addMenuItem(el);
20119             }
20120         }
20121         return item;
20122     },
20123
20124     /**
20125      * Returns this menu's underlying {@link Roo.Element} object
20126      * @return {Roo.Element} The element
20127      */
20128     getEl : function(){
20129         if(!this.el){
20130             this.render();
20131         }
20132         return this.el;
20133     },
20134
20135     /**
20136      * Adds a separator bar to the menu
20137      * @return {Roo.menu.Item} The menu item that was added
20138      */
20139     addSeparator : function(){
20140         return this.addItem(new Roo.menu.Separator());
20141     },
20142
20143     /**
20144      * Adds an {@link Roo.Element} object to the menu
20145      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
20146      * @return {Roo.menu.Item} The menu item that was added
20147      */
20148     addElement : function(el){
20149         return this.addItem(new Roo.menu.BaseItem(el));
20150     },
20151
20152     /**
20153      * Adds an existing object based on {@link Roo.menu.Item} to the menu
20154      * @param {Roo.menu.Item} item The menu item to add
20155      * @return {Roo.menu.Item} The menu item that was added
20156      */
20157     addItem : function(item){
20158         this.items.add(item);
20159         if(this.ul){
20160             var li = document.createElement("li");
20161             li.className = "x-menu-list-item";
20162             this.ul.dom.appendChild(li);
20163             item.render(li, this);
20164             this.delayAutoWidth();
20165         }
20166         return item;
20167     },
20168
20169     /**
20170      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
20171      * @param {Object} config A MenuItem config object
20172      * @return {Roo.menu.Item} The menu item that was added
20173      */
20174     addMenuItem : function(config){
20175         if(!(config instanceof Roo.menu.Item)){
20176             if(typeof config.checked == "boolean"){ // must be check menu item config?
20177                 config = new Roo.menu.CheckItem(config);
20178             }else{
20179                 config = new Roo.menu.Item(config);
20180             }
20181         }
20182         return this.addItem(config);
20183     },
20184
20185     /**
20186      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
20187      * @param {String} text The text to display in the menu item
20188      * @return {Roo.menu.Item} The menu item that was added
20189      */
20190     addText : function(text){
20191         return this.addItem(new Roo.menu.TextItem({ text : text }));
20192     },
20193
20194     /**
20195      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
20196      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
20197      * @param {Roo.menu.Item} item The menu item to add
20198      * @return {Roo.menu.Item} The menu item that was added
20199      */
20200     insert : function(index, item){
20201         this.items.insert(index, item);
20202         if(this.ul){
20203             var li = document.createElement("li");
20204             li.className = "x-menu-list-item";
20205             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
20206             item.render(li, this);
20207             this.delayAutoWidth();
20208         }
20209         return item;
20210     },
20211
20212     /**
20213      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
20214      * @param {Roo.menu.Item} item The menu item to remove
20215      */
20216     remove : function(item){
20217         this.items.removeKey(item.id);
20218         item.destroy();
20219     },
20220
20221     /**
20222      * Removes and destroys all items in the menu
20223      */
20224     removeAll : function(){
20225         var f;
20226         while(f = this.items.first()){
20227             this.remove(f);
20228         }
20229     }
20230 });
20231
20232 // MenuNav is a private utility class used internally by the Menu
20233 Roo.menu.MenuNav = function(menu){
20234     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
20235     this.scope = this.menu = menu;
20236 };
20237
20238 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
20239     doRelay : function(e, h){
20240         var k = e.getKey();
20241         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
20242             this.menu.tryActivate(0, 1);
20243             return false;
20244         }
20245         return h.call(this.scope || this, e, this.menu);
20246     },
20247
20248     up : function(e, m){
20249         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
20250             m.tryActivate(m.items.length-1, -1);
20251         }
20252     },
20253
20254     down : function(e, m){
20255         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
20256             m.tryActivate(0, 1);
20257         }
20258     },
20259
20260     right : function(e, m){
20261         if(m.activeItem){
20262             m.activeItem.expandMenu(true);
20263         }
20264     },
20265
20266     left : function(e, m){
20267         m.hide();
20268         if(m.parentMenu && m.parentMenu.activeItem){
20269             m.parentMenu.activeItem.activate();
20270         }
20271     },
20272
20273     enter : function(e, m){
20274         if(m.activeItem){
20275             e.stopPropagation();
20276             m.activeItem.onClick(e);
20277             m.fireEvent("click", this, m.activeItem);
20278             return true;
20279         }
20280     }
20281 });/*
20282  * Based on:
20283  * Ext JS Library 1.1.1
20284  * Copyright(c) 2006-2007, Ext JS, LLC.
20285  *
20286  * Originally Released Under LGPL - original licence link has changed is not relivant.
20287  *
20288  * Fork - LGPL
20289  * <script type="text/javascript">
20290  */
20291  
20292 /**
20293  * @class Roo.menu.MenuMgr
20294  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
20295  * @singleton
20296  */
20297 Roo.menu.MenuMgr = function(){
20298    var menus, active, groups = {}, attached = false, lastShow = new Date();
20299
20300    // private - called when first menu is created
20301    function init(){
20302        menus = {};
20303        active = new Roo.util.MixedCollection();
20304        Roo.get(document).addKeyListener(27, function(){
20305            if(active.length > 0){
20306                hideAll();
20307            }
20308        });
20309    }
20310
20311    // private
20312    function hideAll(){
20313        if(active && active.length > 0){
20314            var c = active.clone();
20315            c.each(function(m){
20316                m.hide();
20317            });
20318        }
20319    }
20320
20321    // private
20322    function onHide(m){
20323        active.remove(m);
20324        if(active.length < 1){
20325            Roo.get(document).un("mousedown", onMouseDown);
20326            attached = false;
20327        }
20328    }
20329
20330    // private
20331    function onShow(m){
20332        var last = active.last();
20333        lastShow = new Date();
20334        active.add(m);
20335        if(!attached){
20336            Roo.get(document).on("mousedown", onMouseDown);
20337            attached = true;
20338        }
20339        if(m.parentMenu){
20340           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
20341           m.parentMenu.activeChild = m;
20342        }else if(last && last.isVisible()){
20343           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
20344        }
20345    }
20346
20347    // private
20348    function onBeforeHide(m){
20349        if(m.activeChild){
20350            m.activeChild.hide();
20351        }
20352        if(m.autoHideTimer){
20353            clearTimeout(m.autoHideTimer);
20354            delete m.autoHideTimer;
20355        }
20356    }
20357
20358    // private
20359    function onBeforeShow(m){
20360        var pm = m.parentMenu;
20361        if(!pm && !m.allowOtherMenus){
20362            hideAll();
20363        }else if(pm && pm.activeChild && active != m){
20364            pm.activeChild.hide();
20365        }
20366    }
20367
20368    // private
20369    function onMouseDown(e){
20370        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
20371            hideAll();
20372        }
20373    }
20374
20375    // private
20376    function onBeforeCheck(mi, state){
20377        if(state){
20378            var g = groups[mi.group];
20379            for(var i = 0, l = g.length; i < l; i++){
20380                if(g[i] != mi){
20381                    g[i].setChecked(false);
20382                }
20383            }
20384        }
20385    }
20386
20387    return {
20388
20389        /**
20390         * Hides all menus that are currently visible
20391         */
20392        hideAll : function(){
20393             hideAll();  
20394        },
20395
20396        // private
20397        register : function(menu){
20398            if(!menus){
20399                init();
20400            }
20401            menus[menu.id] = menu;
20402            menu.on("beforehide", onBeforeHide);
20403            menu.on("hide", onHide);
20404            menu.on("beforeshow", onBeforeShow);
20405            menu.on("show", onShow);
20406            var g = menu.group;
20407            if(g && menu.events["checkchange"]){
20408                if(!groups[g]){
20409                    groups[g] = [];
20410                }
20411                groups[g].push(menu);
20412                menu.on("checkchange", onCheck);
20413            }
20414        },
20415
20416         /**
20417          * Returns a {@link Roo.menu.Menu} object
20418          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
20419          * be used to generate and return a new Menu instance.
20420          */
20421        get : function(menu){
20422            if(typeof menu == "string"){ // menu id
20423                return menus[menu];
20424            }else if(menu.events){  // menu instance
20425                return menu;
20426            }else if(typeof menu.length == 'number'){ // array of menu items?
20427                return new Roo.menu.Menu({items:menu});
20428            }else{ // otherwise, must be a config
20429                return new Roo.menu.Menu(menu);
20430            }
20431        },
20432
20433        // private
20434        unregister : function(menu){
20435            delete menus[menu.id];
20436            menu.un("beforehide", onBeforeHide);
20437            menu.un("hide", onHide);
20438            menu.un("beforeshow", onBeforeShow);
20439            menu.un("show", onShow);
20440            var g = menu.group;
20441            if(g && menu.events["checkchange"]){
20442                groups[g].remove(menu);
20443                menu.un("checkchange", onCheck);
20444            }
20445        },
20446
20447        // private
20448        registerCheckable : function(menuItem){
20449            var g = menuItem.group;
20450            if(g){
20451                if(!groups[g]){
20452                    groups[g] = [];
20453                }
20454                groups[g].push(menuItem);
20455                menuItem.on("beforecheckchange", onBeforeCheck);
20456            }
20457        },
20458
20459        // private
20460        unregisterCheckable : function(menuItem){
20461            var g = menuItem.group;
20462            if(g){
20463                groups[g].remove(menuItem);
20464                menuItem.un("beforecheckchange", onBeforeCheck);
20465            }
20466        }
20467    };
20468 }();/*
20469  * Based on:
20470  * Ext JS Library 1.1.1
20471  * Copyright(c) 2006-2007, Ext JS, LLC.
20472  *
20473  * Originally Released Under LGPL - original licence link has changed is not relivant.
20474  *
20475  * Fork - LGPL
20476  * <script type="text/javascript">
20477  */
20478  
20479
20480 /**
20481  * @class Roo.menu.BaseItem
20482  * @extends Roo.Component
20483  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
20484  * management and base configuration options shared by all menu components.
20485  * @constructor
20486  * Creates a new BaseItem
20487  * @param {Object} config Configuration options
20488  */
20489 Roo.menu.BaseItem = function(config){
20490     Roo.menu.BaseItem.superclass.constructor.call(this, config);
20491
20492     this.addEvents({
20493         /**
20494          * @event click
20495          * Fires when this item is clicked
20496          * @param {Roo.menu.BaseItem} this
20497          * @param {Roo.EventObject} e
20498          */
20499         click: true,
20500         /**
20501          * @event activate
20502          * Fires when this item is activated
20503          * @param {Roo.menu.BaseItem} this
20504          */
20505         activate : true,
20506         /**
20507          * @event deactivate
20508          * Fires when this item is deactivated
20509          * @param {Roo.menu.BaseItem} this
20510          */
20511         deactivate : true
20512     });
20513
20514     if(this.handler){
20515         this.on("click", this.handler, this.scope, true);
20516     }
20517 };
20518
20519 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
20520     /**
20521      * @cfg {Function} handler
20522      * A function that will handle the click event of this menu item (defaults to undefined)
20523      */
20524     /**
20525      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
20526      */
20527     canActivate : false,
20528     
20529      /**
20530      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
20531      */
20532     hidden: false,
20533     
20534     /**
20535      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
20536      */
20537     activeClass : "x-menu-item-active",
20538     /**
20539      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
20540      */
20541     hideOnClick : true,
20542     /**
20543      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
20544      */
20545     hideDelay : 100,
20546
20547     // private
20548     ctype: "Roo.menu.BaseItem",
20549
20550     // private
20551     actionMode : "container",
20552
20553     // private
20554     render : function(container, parentMenu){
20555         this.parentMenu = parentMenu;
20556         Roo.menu.BaseItem.superclass.render.call(this, container);
20557         this.container.menuItemId = this.id;
20558     },
20559
20560     // private
20561     onRender : function(container, position){
20562         this.el = Roo.get(this.el);
20563         container.dom.appendChild(this.el.dom);
20564     },
20565
20566     // private
20567     onClick : function(e){
20568         if(!this.disabled && this.fireEvent("click", this, e) !== false
20569                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
20570             this.handleClick(e);
20571         }else{
20572             e.stopEvent();
20573         }
20574     },
20575
20576     // private
20577     activate : function(){
20578         if(this.disabled){
20579             return false;
20580         }
20581         var li = this.container;
20582         li.addClass(this.activeClass);
20583         this.region = li.getRegion().adjust(2, 2, -2, -2);
20584         this.fireEvent("activate", this);
20585         return true;
20586     },
20587
20588     // private
20589     deactivate : function(){
20590         this.container.removeClass(this.activeClass);
20591         this.fireEvent("deactivate", this);
20592     },
20593
20594     // private
20595     shouldDeactivate : function(e){
20596         return !this.region || !this.region.contains(e.getPoint());
20597     },
20598
20599     // private
20600     handleClick : function(e){
20601         if(this.hideOnClick){
20602             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
20603         }
20604     },
20605
20606     // private
20607     expandMenu : function(autoActivate){
20608         // do nothing
20609     },
20610
20611     // private
20612     hideMenu : function(){
20613         // do nothing
20614     }
20615 });/*
20616  * Based on:
20617  * Ext JS Library 1.1.1
20618  * Copyright(c) 2006-2007, Ext JS, LLC.
20619  *
20620  * Originally Released Under LGPL - original licence link has changed is not relivant.
20621  *
20622  * Fork - LGPL
20623  * <script type="text/javascript">
20624  */
20625  
20626 /**
20627  * @class Roo.menu.Adapter
20628  * @extends Roo.menu.BaseItem
20629  * 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.
20630  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
20631  * @constructor
20632  * Creates a new Adapter
20633  * @param {Object} config Configuration options
20634  */
20635 Roo.menu.Adapter = function(component, config){
20636     Roo.menu.Adapter.superclass.constructor.call(this, config);
20637     this.component = component;
20638 };
20639 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
20640     // private
20641     canActivate : true,
20642
20643     // private
20644     onRender : function(container, position){
20645         this.component.render(container);
20646         this.el = this.component.getEl();
20647     },
20648
20649     // private
20650     activate : function(){
20651         if(this.disabled){
20652             return false;
20653         }
20654         this.component.focus();
20655         this.fireEvent("activate", this);
20656         return true;
20657     },
20658
20659     // private
20660     deactivate : function(){
20661         this.fireEvent("deactivate", this);
20662     },
20663
20664     // private
20665     disable : function(){
20666         this.component.disable();
20667         Roo.menu.Adapter.superclass.disable.call(this);
20668     },
20669
20670     // private
20671     enable : function(){
20672         this.component.enable();
20673         Roo.menu.Adapter.superclass.enable.call(this);
20674     }
20675 });/*
20676  * Based on:
20677  * Ext JS Library 1.1.1
20678  * Copyright(c) 2006-2007, Ext JS, LLC.
20679  *
20680  * Originally Released Under LGPL - original licence link has changed is not relivant.
20681  *
20682  * Fork - LGPL
20683  * <script type="text/javascript">
20684  */
20685
20686 /**
20687  * @class Roo.menu.TextItem
20688  * @extends Roo.menu.BaseItem
20689  * Adds a static text string to a menu, usually used as either a heading or group separator.
20690  * Note: old style constructor with text is still supported.
20691  * 
20692  * @constructor
20693  * Creates a new TextItem
20694  * @param {Object} cfg Configuration
20695  */
20696 Roo.menu.TextItem = function(cfg){
20697     if (typeof(cfg) == 'string') {
20698         this.text = cfg;
20699     } else {
20700         Roo.apply(this,cfg);
20701     }
20702     
20703     Roo.menu.TextItem.superclass.constructor.call(this);
20704 };
20705
20706 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20707     /**
20708      * @cfg {Boolean} text Text to show on item.
20709      */
20710     text : '',
20711     
20712     /**
20713      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20714      */
20715     hideOnClick : false,
20716     /**
20717      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20718      */
20719     itemCls : "x-menu-text",
20720
20721     // private
20722     onRender : function(){
20723         var s = document.createElement("span");
20724         s.className = this.itemCls;
20725         s.innerHTML = this.text;
20726         this.el = s;
20727         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20728     }
20729 });/*
20730  * Based on:
20731  * Ext JS Library 1.1.1
20732  * Copyright(c) 2006-2007, Ext JS, LLC.
20733  *
20734  * Originally Released Under LGPL - original licence link has changed is not relivant.
20735  *
20736  * Fork - LGPL
20737  * <script type="text/javascript">
20738  */
20739
20740 /**
20741  * @class Roo.menu.Separator
20742  * @extends Roo.menu.BaseItem
20743  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20744  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20745  * @constructor
20746  * @param {Object} config Configuration options
20747  */
20748 Roo.menu.Separator = function(config){
20749     Roo.menu.Separator.superclass.constructor.call(this, config);
20750 };
20751
20752 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20753     /**
20754      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20755      */
20756     itemCls : "x-menu-sep",
20757     /**
20758      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20759      */
20760     hideOnClick : false,
20761
20762     // private
20763     onRender : function(li){
20764         var s = document.createElement("span");
20765         s.className = this.itemCls;
20766         s.innerHTML = "&#160;";
20767         this.el = s;
20768         li.addClass("x-menu-sep-li");
20769         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20770     }
20771 });/*
20772  * Based on:
20773  * Ext JS Library 1.1.1
20774  * Copyright(c) 2006-2007, Ext JS, LLC.
20775  *
20776  * Originally Released Under LGPL - original licence link has changed is not relivant.
20777  *
20778  * Fork - LGPL
20779  * <script type="text/javascript">
20780  */
20781 /**
20782  * @class Roo.menu.Item
20783  * @extends Roo.menu.BaseItem
20784  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20785  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20786  * activation and click handling.
20787  * @constructor
20788  * Creates a new Item
20789  * @param {Object} config Configuration options
20790  */
20791 Roo.menu.Item = function(config){
20792     Roo.menu.Item.superclass.constructor.call(this, config);
20793     if(this.menu){
20794         this.menu = Roo.menu.MenuMgr.get(this.menu);
20795     }
20796 };
20797 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20798     
20799     /**
20800      * @cfg {String} text
20801      * The text to show on the menu item.
20802      */
20803     text: '',
20804      /**
20805      * @cfg {String} HTML to render in menu
20806      * The text to show on the menu item (HTML version).
20807      */
20808     html: '',
20809     /**
20810      * @cfg {String} icon
20811      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20812      */
20813     icon: undefined,
20814     /**
20815      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20816      */
20817     itemCls : "x-menu-item",
20818     /**
20819      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20820      */
20821     canActivate : true,
20822     /**
20823      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20824      */
20825     showDelay: 200,
20826     // doc'd in BaseItem
20827     hideDelay: 200,
20828
20829     // private
20830     ctype: "Roo.menu.Item",
20831     
20832     // private
20833     onRender : function(container, position){
20834         var el = document.createElement("a");
20835         el.hideFocus = true;
20836         el.unselectable = "on";
20837         el.href = this.href || "#";
20838         if(this.hrefTarget){
20839             el.target = this.hrefTarget;
20840         }
20841         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
20842         
20843         var html = this.html.length ? this.html  : String.format('{0}',this.text);
20844         
20845         el.innerHTML = String.format(
20846                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
20847                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
20848         this.el = el;
20849         Roo.menu.Item.superclass.onRender.call(this, container, position);
20850     },
20851
20852     /**
20853      * Sets the text to display in this menu item
20854      * @param {String} text The text to display
20855      * @param {Boolean} isHTML true to indicate text is pure html.
20856      */
20857     setText : function(text, isHTML){
20858         if (isHTML) {
20859             this.html = text;
20860         } else {
20861             this.text = text;
20862             this.html = '';
20863         }
20864         if(this.rendered){
20865             var html = this.html.length ? this.html  : String.format('{0}',this.text);
20866      
20867             this.el.update(String.format(
20868                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
20869                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20870             this.parentMenu.autoWidth();
20871         }
20872     },
20873
20874     // private
20875     handleClick : function(e){
20876         if(!this.href){ // if no link defined, stop the event automatically
20877             e.stopEvent();
20878         }
20879         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20880     },
20881
20882     // private
20883     activate : function(autoExpand){
20884         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20885             this.focus();
20886             if(autoExpand){
20887                 this.expandMenu();
20888             }
20889         }
20890         return true;
20891     },
20892
20893     // private
20894     shouldDeactivate : function(e){
20895         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20896             if(this.menu && this.menu.isVisible()){
20897                 return !this.menu.getEl().getRegion().contains(e.getPoint());
20898             }
20899             return true;
20900         }
20901         return false;
20902     },
20903
20904     // private
20905     deactivate : function(){
20906         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20907         this.hideMenu();
20908     },
20909
20910     // private
20911     expandMenu : function(autoActivate){
20912         if(!this.disabled && this.menu){
20913             clearTimeout(this.hideTimer);
20914             delete this.hideTimer;
20915             if(!this.menu.isVisible() && !this.showTimer){
20916                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20917             }else if (this.menu.isVisible() && autoActivate){
20918                 this.menu.tryActivate(0, 1);
20919             }
20920         }
20921     },
20922
20923     // private
20924     deferExpand : function(autoActivate){
20925         delete this.showTimer;
20926         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20927         if(autoActivate){
20928             this.menu.tryActivate(0, 1);
20929         }
20930     },
20931
20932     // private
20933     hideMenu : function(){
20934         clearTimeout(this.showTimer);
20935         delete this.showTimer;
20936         if(!this.hideTimer && this.menu && this.menu.isVisible()){
20937             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20938         }
20939     },
20940
20941     // private
20942     deferHide : function(){
20943         delete this.hideTimer;
20944         this.menu.hide();
20945     }
20946 });/*
20947  * Based on:
20948  * Ext JS Library 1.1.1
20949  * Copyright(c) 2006-2007, Ext JS, LLC.
20950  *
20951  * Originally Released Under LGPL - original licence link has changed is not relivant.
20952  *
20953  * Fork - LGPL
20954  * <script type="text/javascript">
20955  */
20956  
20957 /**
20958  * @class Roo.menu.CheckItem
20959  * @extends Roo.menu.Item
20960  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20961  * @constructor
20962  * Creates a new CheckItem
20963  * @param {Object} config Configuration options
20964  */
20965 Roo.menu.CheckItem = function(config){
20966     Roo.menu.CheckItem.superclass.constructor.call(this, config);
20967     this.addEvents({
20968         /**
20969          * @event beforecheckchange
20970          * Fires before the checked value is set, providing an opportunity to cancel if needed
20971          * @param {Roo.menu.CheckItem} this
20972          * @param {Boolean} checked The new checked value that will be set
20973          */
20974         "beforecheckchange" : true,
20975         /**
20976          * @event checkchange
20977          * Fires after the checked value has been set
20978          * @param {Roo.menu.CheckItem} this
20979          * @param {Boolean} checked The checked value that was set
20980          */
20981         "checkchange" : true
20982     });
20983     if(this.checkHandler){
20984         this.on('checkchange', this.checkHandler, this.scope);
20985     }
20986 };
20987 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
20988     /**
20989      * @cfg {String} group
20990      * All check items with the same group name will automatically be grouped into a single-select
20991      * radio button group (defaults to '')
20992      */
20993     /**
20994      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
20995      */
20996     itemCls : "x-menu-item x-menu-check-item",
20997     /**
20998      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
20999      */
21000     groupClass : "x-menu-group-item",
21001
21002     /**
21003      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
21004      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
21005      * initialized with checked = true will be rendered as checked.
21006      */
21007     checked: false,
21008
21009     // private
21010     ctype: "Roo.menu.CheckItem",
21011
21012     // private
21013     onRender : function(c){
21014         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
21015         if(this.group){
21016             this.el.addClass(this.groupClass);
21017         }
21018         Roo.menu.MenuMgr.registerCheckable(this);
21019         if(this.checked){
21020             this.checked = false;
21021             this.setChecked(true, true);
21022         }
21023     },
21024
21025     // private
21026     destroy : function(){
21027         if(this.rendered){
21028             Roo.menu.MenuMgr.unregisterCheckable(this);
21029         }
21030         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
21031     },
21032
21033     /**
21034      * Set the checked state of this item
21035      * @param {Boolean} checked The new checked value
21036      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
21037      */
21038     setChecked : function(state, suppressEvent){
21039         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
21040             if(this.container){
21041                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
21042             }
21043             this.checked = state;
21044             if(suppressEvent !== true){
21045                 this.fireEvent("checkchange", this, state);
21046             }
21047         }
21048     },
21049
21050     // private
21051     handleClick : function(e){
21052        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
21053            this.setChecked(!this.checked);
21054        }
21055        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
21056     }
21057 });/*
21058  * Based on:
21059  * Ext JS Library 1.1.1
21060  * Copyright(c) 2006-2007, Ext JS, LLC.
21061  *
21062  * Originally Released Under LGPL - original licence link has changed is not relivant.
21063  *
21064  * Fork - LGPL
21065  * <script type="text/javascript">
21066  */
21067  
21068 /**
21069  * @class Roo.menu.DateItem
21070  * @extends Roo.menu.Adapter
21071  * A menu item that wraps the {@link Roo.DatPicker} component.
21072  * @constructor
21073  * Creates a new DateItem
21074  * @param {Object} config Configuration options
21075  */
21076 Roo.menu.DateItem = function(config){
21077     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
21078     /** The Roo.DatePicker object @type Roo.DatePicker */
21079     this.picker = this.component;
21080     this.addEvents({select: true});
21081     
21082     this.picker.on("render", function(picker){
21083         picker.getEl().swallowEvent("click");
21084         picker.container.addClass("x-menu-date-item");
21085     });
21086
21087     this.picker.on("select", this.onSelect, this);
21088 };
21089
21090 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
21091     // private
21092     onSelect : function(picker, date){
21093         this.fireEvent("select", this, date, picker);
21094         Roo.menu.DateItem.superclass.handleClick.call(this);
21095     }
21096 });/*
21097  * Based on:
21098  * Ext JS Library 1.1.1
21099  * Copyright(c) 2006-2007, Ext JS, LLC.
21100  *
21101  * Originally Released Under LGPL - original licence link has changed is not relivant.
21102  *
21103  * Fork - LGPL
21104  * <script type="text/javascript">
21105  */
21106  
21107 /**
21108  * @class Roo.menu.ColorItem
21109  * @extends Roo.menu.Adapter
21110  * A menu item that wraps the {@link Roo.ColorPalette} component.
21111  * @constructor
21112  * Creates a new ColorItem
21113  * @param {Object} config Configuration options
21114  */
21115 Roo.menu.ColorItem = function(config){
21116     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
21117     /** The Roo.ColorPalette object @type Roo.ColorPalette */
21118     this.palette = this.component;
21119     this.relayEvents(this.palette, ["select"]);
21120     if(this.selectHandler){
21121         this.on('select', this.selectHandler, this.scope);
21122     }
21123 };
21124 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
21125  * Based on:
21126  * Ext JS Library 1.1.1
21127  * Copyright(c) 2006-2007, Ext JS, LLC.
21128  *
21129  * Originally Released Under LGPL - original licence link has changed is not relivant.
21130  *
21131  * Fork - LGPL
21132  * <script type="text/javascript">
21133  */
21134  
21135
21136 /**
21137  * @class Roo.menu.DateMenu
21138  * @extends Roo.menu.Menu
21139  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
21140  * @constructor
21141  * Creates a new DateMenu
21142  * @param {Object} config Configuration options
21143  */
21144 Roo.menu.DateMenu = function(config){
21145     Roo.menu.DateMenu.superclass.constructor.call(this, config);
21146     this.plain = true;
21147     var di = new Roo.menu.DateItem(config);
21148     this.add(di);
21149     /**
21150      * The {@link Roo.DatePicker} instance for this DateMenu
21151      * @type DatePicker
21152      */
21153     this.picker = di.picker;
21154     /**
21155      * @event select
21156      * @param {DatePicker} picker
21157      * @param {Date} date
21158      */
21159     this.relayEvents(di, ["select"]);
21160     this.on('beforeshow', function(){
21161         if(this.picker){
21162             this.picker.hideMonthPicker(false);
21163         }
21164     }, this);
21165 };
21166 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
21167     cls:'x-date-menu'
21168 });/*
21169  * Based on:
21170  * Ext JS Library 1.1.1
21171  * Copyright(c) 2006-2007, Ext JS, LLC.
21172  *
21173  * Originally Released Under LGPL - original licence link has changed is not relivant.
21174  *
21175  * Fork - LGPL
21176  * <script type="text/javascript">
21177  */
21178  
21179
21180 /**
21181  * @class Roo.menu.ColorMenu
21182  * @extends Roo.menu.Menu
21183  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
21184  * @constructor
21185  * Creates a new ColorMenu
21186  * @param {Object} config Configuration options
21187  */
21188 Roo.menu.ColorMenu = function(config){
21189     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
21190     this.plain = true;
21191     var ci = new Roo.menu.ColorItem(config);
21192     this.add(ci);
21193     /**
21194      * The {@link Roo.ColorPalette} instance for this ColorMenu
21195      * @type ColorPalette
21196      */
21197     this.palette = ci.palette;
21198     /**
21199      * @event select
21200      * @param {ColorPalette} palette
21201      * @param {String} color
21202      */
21203     this.relayEvents(ci, ["select"]);
21204 };
21205 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
21206  * Based on:
21207  * Ext JS Library 1.1.1
21208  * Copyright(c) 2006-2007, Ext JS, LLC.
21209  *
21210  * Originally Released Under LGPL - original licence link has changed is not relivant.
21211  *
21212  * Fork - LGPL
21213  * <script type="text/javascript">
21214  */
21215  
21216 /**
21217  * @class Roo.form.Field
21218  * @extends Roo.BoxComponent
21219  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
21220  * @constructor
21221  * Creates a new Field
21222  * @param {Object} config Configuration options
21223  */
21224 Roo.form.Field = function(config){
21225     Roo.form.Field.superclass.constructor.call(this, config);
21226 };
21227
21228 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
21229     /**
21230      * @cfg {String} fieldLabel Label to use when rendering a form.
21231      */
21232        /**
21233      * @cfg {String} qtip Mouse over tip
21234      */
21235      
21236     /**
21237      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
21238      */
21239     invalidClass : "x-form-invalid",
21240     /**
21241      * @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")
21242      */
21243     invalidText : "The value in this field is invalid",
21244     /**
21245      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
21246      */
21247     focusClass : "x-form-focus",
21248     /**
21249      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
21250       automatic validation (defaults to "keyup").
21251      */
21252     validationEvent : "keyup",
21253     /**
21254      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
21255      */
21256     validateOnBlur : true,
21257     /**
21258      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
21259      */
21260     validationDelay : 250,
21261     /**
21262      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21263      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
21264      */
21265     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
21266     /**
21267      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
21268      */
21269     fieldClass : "x-form-field",
21270     /**
21271      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
21272      *<pre>
21273 Value         Description
21274 -----------   ----------------------------------------------------------------------
21275 qtip          Display a quick tip when the user hovers over the field
21276 title         Display a default browser title attribute popup
21277 under         Add a block div beneath the field containing the error text
21278 side          Add an error icon to the right of the field with a popup on hover
21279 [element id]  Add the error text directly to the innerHTML of the specified element
21280 </pre>
21281      */
21282     msgTarget : 'qtip',
21283     /**
21284      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
21285      */
21286     msgFx : 'normal',
21287
21288     /**
21289      * @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.
21290      */
21291     readOnly : false,
21292
21293     /**
21294      * @cfg {Boolean} disabled True to disable the field (defaults to false).
21295      */
21296     disabled : false,
21297
21298     /**
21299      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
21300      */
21301     inputType : undefined,
21302     
21303     /**
21304      * @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).
21305          */
21306         tabIndex : undefined,
21307         
21308     // private
21309     isFormField : true,
21310
21311     // private
21312     hasFocus : false,
21313     /**
21314      * @property {Roo.Element} fieldEl
21315      * Element Containing the rendered Field (with label etc.)
21316      */
21317     /**
21318      * @cfg {Mixed} value A value to initialize this field with.
21319      */
21320     value : undefined,
21321
21322     /**
21323      * @cfg {String} name The field's HTML name attribute.
21324      */
21325     /**
21326      * @cfg {String} cls A CSS class to apply to the field's underlying element.
21327      */
21328
21329         // private ??
21330         initComponent : function(){
21331         Roo.form.Field.superclass.initComponent.call(this);
21332         this.addEvents({
21333             /**
21334              * @event focus
21335              * Fires when this field receives input focus.
21336              * @param {Roo.form.Field} this
21337              */
21338             focus : true,
21339             /**
21340              * @event blur
21341              * Fires when this field loses input focus.
21342              * @param {Roo.form.Field} this
21343              */
21344             blur : true,
21345             /**
21346              * @event specialkey
21347              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
21348              * {@link Roo.EventObject#getKey} to determine which key was pressed.
21349              * @param {Roo.form.Field} this
21350              * @param {Roo.EventObject} e The event object
21351              */
21352             specialkey : true,
21353             /**
21354              * @event change
21355              * Fires just before the field blurs if the field value has changed.
21356              * @param {Roo.form.Field} this
21357              * @param {Mixed} newValue The new value
21358              * @param {Mixed} oldValue The original value
21359              */
21360             change : true,
21361             /**
21362              * @event invalid
21363              * Fires after the field has been marked as invalid.
21364              * @param {Roo.form.Field} this
21365              * @param {String} msg The validation message
21366              */
21367             invalid : true,
21368             /**
21369              * @event valid
21370              * Fires after the field has been validated with no errors.
21371              * @param {Roo.form.Field} this
21372              */
21373             valid : true,
21374              /**
21375              * @event keyup
21376              * Fires after the key up
21377              * @param {Roo.form.Field} this
21378              * @param {Roo.EventObject}  e The event Object
21379              */
21380             keyup : true
21381         });
21382     },
21383
21384     /**
21385      * Returns the name attribute of the field if available
21386      * @return {String} name The field name
21387      */
21388     getName: function(){
21389          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
21390     },
21391
21392     // private
21393     onRender : function(ct, position){
21394         Roo.form.Field.superclass.onRender.call(this, ct, position);
21395         if(!this.el){
21396             var cfg = this.getAutoCreate();
21397             if(!cfg.name){
21398                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
21399             }
21400             if (!cfg.name.length) {
21401                 delete cfg.name;
21402             }
21403             if(this.inputType){
21404                 cfg.type = this.inputType;
21405             }
21406             this.el = ct.createChild(cfg, position);
21407         }
21408         var type = this.el.dom.type;
21409         if(type){
21410             if(type == 'password'){
21411                 type = 'text';
21412             }
21413             this.el.addClass('x-form-'+type);
21414         }
21415         if(this.readOnly){
21416             this.el.dom.readOnly = true;
21417         }
21418         if(this.tabIndex !== undefined){
21419             this.el.dom.setAttribute('tabIndex', this.tabIndex);
21420         }
21421
21422         this.el.addClass([this.fieldClass, this.cls]);
21423         this.initValue();
21424     },
21425
21426     /**
21427      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
21428      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
21429      * @return {Roo.form.Field} this
21430      */
21431     applyTo : function(target){
21432         this.allowDomMove = false;
21433         this.el = Roo.get(target);
21434         this.render(this.el.dom.parentNode);
21435         return this;
21436     },
21437
21438     // private
21439     initValue : function(){
21440         if(this.value !== undefined){
21441             this.setValue(this.value);
21442         }else if(this.el.dom.value.length > 0){
21443             this.setValue(this.el.dom.value);
21444         }
21445     },
21446
21447     /**
21448      * Returns true if this field has been changed since it was originally loaded and is not disabled.
21449      */
21450     isDirty : function() {
21451         if(this.disabled) {
21452             return false;
21453         }
21454         return String(this.getValue()) !== String(this.originalValue);
21455     },
21456
21457     // private
21458     afterRender : function(){
21459         Roo.form.Field.superclass.afterRender.call(this);
21460         this.initEvents();
21461     },
21462
21463     // private
21464     fireKey : function(e){
21465         //Roo.log('field ' + e.getKey());
21466         if(e.isNavKeyPress()){
21467             this.fireEvent("specialkey", this, e);
21468         }
21469     },
21470
21471     /**
21472      * Resets the current field value to the originally loaded value and clears any validation messages
21473      */
21474     reset : function(){
21475         this.setValue(this.originalValue);
21476         this.clearInvalid();
21477     },
21478
21479     // private
21480     initEvents : function(){
21481         // safari killled keypress - so keydown is now used..
21482         this.el.on("keydown" , this.fireKey,  this);
21483         this.el.on("focus", this.onFocus,  this);
21484         this.el.on("blur", this.onBlur,  this);
21485         this.el.relayEvent('keyup', this);
21486
21487         // reference to original value for reset
21488         this.originalValue = this.getValue();
21489     },
21490
21491     // private
21492     onFocus : function(){
21493         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21494             this.el.addClass(this.focusClass);
21495         }
21496         if(!this.hasFocus){
21497             this.hasFocus = true;
21498             this.startValue = this.getValue();
21499             this.fireEvent("focus", this);
21500         }
21501     },
21502
21503     beforeBlur : Roo.emptyFn,
21504
21505     // private
21506     onBlur : function(){
21507         this.beforeBlur();
21508         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21509             this.el.removeClass(this.focusClass);
21510         }
21511         this.hasFocus = false;
21512         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
21513             this.validate();
21514         }
21515         var v = this.getValue();
21516         if(String(v) !== String(this.startValue)){
21517             this.fireEvent('change', this, v, this.startValue);
21518         }
21519         this.fireEvent("blur", this);
21520     },
21521
21522     /**
21523      * Returns whether or not the field value is currently valid
21524      * @param {Boolean} preventMark True to disable marking the field invalid
21525      * @return {Boolean} True if the value is valid, else false
21526      */
21527     isValid : function(preventMark){
21528         if(this.disabled){
21529             return true;
21530         }
21531         var restore = this.preventMark;
21532         this.preventMark = preventMark === true;
21533         var v = this.validateValue(this.processValue(this.getRawValue()));
21534         this.preventMark = restore;
21535         return v;
21536     },
21537
21538     /**
21539      * Validates the field value
21540      * @return {Boolean} True if the value is valid, else false
21541      */
21542     validate : function(){
21543         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
21544             this.clearInvalid();
21545             return true;
21546         }
21547         return false;
21548     },
21549
21550     processValue : function(value){
21551         return value;
21552     },
21553
21554     // private
21555     // Subclasses should provide the validation implementation by overriding this
21556     validateValue : function(value){
21557         return true;
21558     },
21559
21560     /**
21561      * Mark this field as invalid
21562      * @param {String} msg The validation message
21563      */
21564     markInvalid : function(msg){
21565         if(!this.rendered || this.preventMark){ // not rendered
21566             return;
21567         }
21568         this.el.addClass(this.invalidClass);
21569         msg = msg || this.invalidText;
21570         switch(this.msgTarget){
21571             case 'qtip':
21572                 this.el.dom.qtip = msg;
21573                 this.el.dom.qclass = 'x-form-invalid-tip';
21574                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
21575                     Roo.QuickTips.enable();
21576                 }
21577                 break;
21578             case 'title':
21579                 this.el.dom.title = msg;
21580                 break;
21581             case 'under':
21582                 if(!this.errorEl){
21583                     var elp = this.el.findParent('.x-form-element', 5, true);
21584                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
21585                     this.errorEl.setWidth(elp.getWidth(true)-20);
21586                 }
21587                 this.errorEl.update(msg);
21588                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
21589                 break;
21590             case 'side':
21591                 if(!this.errorIcon){
21592                     var elp = this.el.findParent('.x-form-element', 5, true);
21593                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
21594                 }
21595                 this.alignErrorIcon();
21596                 this.errorIcon.dom.qtip = msg;
21597                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
21598                 this.errorIcon.show();
21599                 this.on('resize', this.alignErrorIcon, this);
21600                 break;
21601             default:
21602                 var t = Roo.getDom(this.msgTarget);
21603                 t.innerHTML = msg;
21604                 t.style.display = this.msgDisplay;
21605                 break;
21606         }
21607         this.fireEvent('invalid', this, msg);
21608     },
21609
21610     // private
21611     alignErrorIcon : function(){
21612         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
21613     },
21614
21615     /**
21616      * Clear any invalid styles/messages for this field
21617      */
21618     clearInvalid : function(){
21619         if(!this.rendered || this.preventMark){ // not rendered
21620             return;
21621         }
21622         this.el.removeClass(this.invalidClass);
21623         switch(this.msgTarget){
21624             case 'qtip':
21625                 this.el.dom.qtip = '';
21626                 break;
21627             case 'title':
21628                 this.el.dom.title = '';
21629                 break;
21630             case 'under':
21631                 if(this.errorEl){
21632                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
21633                 }
21634                 break;
21635             case 'side':
21636                 if(this.errorIcon){
21637                     this.errorIcon.dom.qtip = '';
21638                     this.errorIcon.hide();
21639                     this.un('resize', this.alignErrorIcon, this);
21640                 }
21641                 break;
21642             default:
21643                 var t = Roo.getDom(this.msgTarget);
21644                 t.innerHTML = '';
21645                 t.style.display = 'none';
21646                 break;
21647         }
21648         this.fireEvent('valid', this);
21649     },
21650
21651     /**
21652      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
21653      * @return {Mixed} value The field value
21654      */
21655     getRawValue : function(){
21656         var v = this.el.getValue();
21657         
21658         return v;
21659     },
21660
21661     /**
21662      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
21663      * @return {Mixed} value The field value
21664      */
21665     getValue : function(){
21666         var v = this.el.getValue();
21667          
21668         return v;
21669     },
21670
21671     /**
21672      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
21673      * @param {Mixed} value The value to set
21674      */
21675     setRawValue : function(v){
21676         return this.el.dom.value = (v === null || v === undefined ? '' : v);
21677     },
21678
21679     /**
21680      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
21681      * @param {Mixed} value The value to set
21682      */
21683     setValue : function(v){
21684         this.value = v;
21685         if(this.rendered){
21686             this.el.dom.value = (v === null || v === undefined ? '' : v);
21687              this.validate();
21688         }
21689     },
21690
21691     adjustSize : function(w, h){
21692         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21693         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21694         return s;
21695     },
21696
21697     adjustWidth : function(tag, w){
21698         tag = tag.toLowerCase();
21699         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21700             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21701                 if(tag == 'input'){
21702                     return w + 2;
21703                 }
21704                 if(tag == 'textarea'){
21705                     return w-2;
21706                 }
21707             }else if(Roo.isOpera){
21708                 if(tag == 'input'){
21709                     return w + 2;
21710                 }
21711                 if(tag == 'textarea'){
21712                     return w-2;
21713                 }
21714             }
21715         }
21716         return w;
21717     }
21718 });
21719
21720
21721 // anything other than normal should be considered experimental
21722 Roo.form.Field.msgFx = {
21723     normal : {
21724         show: function(msgEl, f){
21725             msgEl.setDisplayed('block');
21726         },
21727
21728         hide : function(msgEl, f){
21729             msgEl.setDisplayed(false).update('');
21730         }
21731     },
21732
21733     slide : {
21734         show: function(msgEl, f){
21735             msgEl.slideIn('t', {stopFx:true});
21736         },
21737
21738         hide : function(msgEl, f){
21739             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21740         }
21741     },
21742
21743     slideRight : {
21744         show: function(msgEl, f){
21745             msgEl.fixDisplay();
21746             msgEl.alignTo(f.el, 'tl-tr');
21747             msgEl.slideIn('l', {stopFx:true});
21748         },
21749
21750         hide : function(msgEl, f){
21751             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21752         }
21753     }
21754 };/*
21755  * Based on:
21756  * Ext JS Library 1.1.1
21757  * Copyright(c) 2006-2007, Ext JS, LLC.
21758  *
21759  * Originally Released Under LGPL - original licence link has changed is not relivant.
21760  *
21761  * Fork - LGPL
21762  * <script type="text/javascript">
21763  */
21764  
21765
21766 /**
21767  * @class Roo.form.TextField
21768  * @extends Roo.form.Field
21769  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
21770  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21771  * @constructor
21772  * Creates a new TextField
21773  * @param {Object} config Configuration options
21774  */
21775 Roo.form.TextField = function(config){
21776     Roo.form.TextField.superclass.constructor.call(this, config);
21777     this.addEvents({
21778         /**
21779          * @event autosize
21780          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
21781          * according to the default logic, but this event provides a hook for the developer to apply additional
21782          * logic at runtime to resize the field if needed.
21783              * @param {Roo.form.Field} this This text field
21784              * @param {Number} width The new field width
21785              */
21786         autosize : true
21787     });
21788 };
21789
21790 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
21791     /**
21792      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21793      */
21794     grow : false,
21795     /**
21796      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21797      */
21798     growMin : 30,
21799     /**
21800      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21801      */
21802     growMax : 800,
21803     /**
21804      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21805      */
21806     vtype : null,
21807     /**
21808      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21809      */
21810     maskRe : null,
21811     /**
21812      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21813      */
21814     disableKeyFilter : false,
21815     /**
21816      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21817      */
21818     allowBlank : true,
21819     /**
21820      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21821      */
21822     minLength : 0,
21823     /**
21824      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21825      */
21826     maxLength : Number.MAX_VALUE,
21827     /**
21828      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21829      */
21830     minLengthText : "The minimum length for this field is {0}",
21831     /**
21832      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21833      */
21834     maxLengthText : "The maximum length for this field is {0}",
21835     /**
21836      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21837      */
21838     selectOnFocus : false,
21839     /**
21840      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21841      */
21842     blankText : "This field is required",
21843     /**
21844      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21845      * If available, this function will be called only after the basic validators all return true, and will be passed the
21846      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21847      */
21848     validator : null,
21849     /**
21850      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21851      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21852      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
21853      */
21854     regex : null,
21855     /**
21856      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21857      */
21858     regexText : "",
21859     /**
21860      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
21861      */
21862     emptyText : null,
21863    
21864
21865     // private
21866     initEvents : function()
21867     {
21868         if (this.emptyText) {
21869             this.el.attr('placeholder', this.emptyText);
21870         }
21871         
21872         Roo.form.TextField.superclass.initEvents.call(this);
21873         if(this.validationEvent == 'keyup'){
21874             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21875             this.el.on('keyup', this.filterValidation, this);
21876         }
21877         else if(this.validationEvent !== false){
21878             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21879         }
21880         
21881         if(this.selectOnFocus){
21882             this.on("focus", this.preFocus, this);
21883             
21884         }
21885         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21886             this.el.on("keypress", this.filterKeys, this);
21887         }
21888         if(this.grow){
21889             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
21890             this.el.on("click", this.autoSize,  this);
21891         }
21892         if(this.el.is('input[type=password]') && Roo.isSafari){
21893             this.el.on('keydown', this.SafariOnKeyDown, this);
21894         }
21895     },
21896
21897     processValue : function(value){
21898         if(this.stripCharsRe){
21899             var newValue = value.replace(this.stripCharsRe, '');
21900             if(newValue !== value){
21901                 this.setRawValue(newValue);
21902                 return newValue;
21903             }
21904         }
21905         return value;
21906     },
21907
21908     filterValidation : function(e){
21909         if(!e.isNavKeyPress()){
21910             this.validationTask.delay(this.validationDelay);
21911         }
21912     },
21913
21914     // private
21915     onKeyUp : function(e){
21916         if(!e.isNavKeyPress()){
21917             this.autoSize();
21918         }
21919     },
21920
21921     /**
21922      * Resets the current field value to the originally-loaded value and clears any validation messages.
21923      *  
21924      */
21925     reset : function(){
21926         Roo.form.TextField.superclass.reset.call(this);
21927        
21928     },
21929
21930     
21931     // private
21932     preFocus : function(){
21933         
21934         if(this.selectOnFocus){
21935             this.el.dom.select();
21936         }
21937     },
21938
21939     
21940     // private
21941     filterKeys : function(e){
21942         var k = e.getKey();
21943         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21944             return;
21945         }
21946         var c = e.getCharCode(), cc = String.fromCharCode(c);
21947         if(Roo.isIE && (e.isSpecialKey() || !cc)){
21948             return;
21949         }
21950         if(!this.maskRe.test(cc)){
21951             e.stopEvent();
21952         }
21953     },
21954
21955     setValue : function(v){
21956         
21957         Roo.form.TextField.superclass.setValue.apply(this, arguments);
21958         
21959         this.autoSize();
21960     },
21961
21962     /**
21963      * Validates a value according to the field's validation rules and marks the field as invalid
21964      * if the validation fails
21965      * @param {Mixed} value The value to validate
21966      * @return {Boolean} True if the value is valid, else false
21967      */
21968     validateValue : function(value){
21969         if(value.length < 1)  { // if it's blank
21970              if(this.allowBlank){
21971                 this.clearInvalid();
21972                 return true;
21973              }else{
21974                 this.markInvalid(this.blankText);
21975                 return false;
21976              }
21977         }
21978         if(value.length < this.minLength){
21979             this.markInvalid(String.format(this.minLengthText, this.minLength));
21980             return false;
21981         }
21982         if(value.length > this.maxLength){
21983             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
21984             return false;
21985         }
21986         if(this.vtype){
21987             var vt = Roo.form.VTypes;
21988             if(!vt[this.vtype](value, this)){
21989                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
21990                 return false;
21991             }
21992         }
21993         if(typeof this.validator == "function"){
21994             var msg = this.validator(value);
21995             if(msg !== true){
21996                 this.markInvalid(msg);
21997                 return false;
21998             }
21999         }
22000         if(this.regex && !this.regex.test(value)){
22001             this.markInvalid(this.regexText);
22002             return false;
22003         }
22004         return true;
22005     },
22006
22007     /**
22008      * Selects text in this field
22009      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
22010      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
22011      */
22012     selectText : function(start, end){
22013         var v = this.getRawValue();
22014         if(v.length > 0){
22015             start = start === undefined ? 0 : start;
22016             end = end === undefined ? v.length : end;
22017             var d = this.el.dom;
22018             if(d.setSelectionRange){
22019                 d.setSelectionRange(start, end);
22020             }else if(d.createTextRange){
22021                 var range = d.createTextRange();
22022                 range.moveStart("character", start);
22023                 range.moveEnd("character", v.length-end);
22024                 range.select();
22025             }
22026         }
22027     },
22028
22029     /**
22030      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
22031      * This only takes effect if grow = true, and fires the autosize event.
22032      */
22033     autoSize : function(){
22034         if(!this.grow || !this.rendered){
22035             return;
22036         }
22037         if(!this.metrics){
22038             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
22039         }
22040         var el = this.el;
22041         var v = el.dom.value;
22042         var d = document.createElement('div');
22043         d.appendChild(document.createTextNode(v));
22044         v = d.innerHTML;
22045         d = null;
22046         v += "&#160;";
22047         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
22048         this.el.setWidth(w);
22049         this.fireEvent("autosize", this, w);
22050     },
22051     
22052     // private
22053     SafariOnKeyDown : function(event)
22054     {
22055         // this is a workaround for a password hang bug on chrome/ webkit.
22056         
22057         var isSelectAll = false;
22058         
22059         if(this.el.dom.selectionEnd > 0){
22060             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
22061         }
22062         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
22063             event.preventDefault();
22064             this.setValue('');
22065             return;
22066         }
22067         
22068         if(isSelectAll){ // backspace and delete key
22069             
22070             event.preventDefault();
22071             // this is very hacky as keydown always get's upper case.
22072             //
22073             var cc = String.fromCharCode(event.getCharCode());
22074             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
22075             
22076         }
22077         
22078         
22079     }
22080 });/*
22081  * Based on:
22082  * Ext JS Library 1.1.1
22083  * Copyright(c) 2006-2007, Ext JS, LLC.
22084  *
22085  * Originally Released Under LGPL - original licence link has changed is not relivant.
22086  *
22087  * Fork - LGPL
22088  * <script type="text/javascript">
22089  */
22090  
22091 /**
22092  * @class Roo.form.Hidden
22093  * @extends Roo.form.TextField
22094  * Simple Hidden element used on forms 
22095  * 
22096  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
22097  * 
22098  * @constructor
22099  * Creates a new Hidden form element.
22100  * @param {Object} config Configuration options
22101  */
22102
22103
22104
22105 // easy hidden field...
22106 Roo.form.Hidden = function(config){
22107     Roo.form.Hidden.superclass.constructor.call(this, config);
22108 };
22109   
22110 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
22111     fieldLabel:      '',
22112     inputType:      'hidden',
22113     width:          50,
22114     allowBlank:     true,
22115     labelSeparator: '',
22116     hidden:         true,
22117     itemCls :       'x-form-item-display-none'
22118
22119
22120 });
22121
22122
22123 /*
22124  * Based on:
22125  * Ext JS Library 1.1.1
22126  * Copyright(c) 2006-2007, Ext JS, LLC.
22127  *
22128  * Originally Released Under LGPL - original licence link has changed is not relivant.
22129  *
22130  * Fork - LGPL
22131  * <script type="text/javascript">
22132  */
22133  
22134 /**
22135  * @class Roo.form.TriggerField
22136  * @extends Roo.form.TextField
22137  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
22138  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
22139  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
22140  * for which you can provide a custom implementation.  For example:
22141  * <pre><code>
22142 var trigger = new Roo.form.TriggerField();
22143 trigger.onTriggerClick = myTriggerFn;
22144 trigger.applyTo('my-field');
22145 </code></pre>
22146  *
22147  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
22148  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
22149  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
22150  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
22151  * @constructor
22152  * Create a new TriggerField.
22153  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
22154  * to the base TextField)
22155  */
22156 Roo.form.TriggerField = function(config){
22157     this.mimicing = false;
22158     Roo.form.TriggerField.superclass.constructor.call(this, config);
22159 };
22160
22161 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
22162     /**
22163      * @cfg {String} triggerClass A CSS class to apply to the trigger
22164      */
22165     /**
22166      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
22167      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
22168      */
22169     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
22170     /**
22171      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
22172      */
22173     hideTrigger:false,
22174
22175     /** @cfg {Boolean} grow @hide */
22176     /** @cfg {Number} growMin @hide */
22177     /** @cfg {Number} growMax @hide */
22178
22179     /**
22180      * @hide 
22181      * @method
22182      */
22183     autoSize: Roo.emptyFn,
22184     // private
22185     monitorTab : true,
22186     // private
22187     deferHeight : true,
22188
22189     
22190     actionMode : 'wrap',
22191     // private
22192     onResize : function(w, h){
22193         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
22194         if(typeof w == 'number'){
22195             var x = w - this.trigger.getWidth();
22196             this.el.setWidth(this.adjustWidth('input', x));
22197             this.trigger.setStyle('left', x+'px');
22198         }
22199     },
22200
22201     // private
22202     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22203
22204     // private
22205     getResizeEl : function(){
22206         return this.wrap;
22207     },
22208
22209     // private
22210     getPositionEl : function(){
22211         return this.wrap;
22212     },
22213
22214     // private
22215     alignErrorIcon : function(){
22216         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
22217     },
22218
22219     // private
22220     onRender : function(ct, position){
22221         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
22222         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
22223         this.trigger = this.wrap.createChild(this.triggerConfig ||
22224                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
22225         if(this.hideTrigger){
22226             this.trigger.setDisplayed(false);
22227         }
22228         this.initTrigger();
22229         if(!this.width){
22230             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
22231         }
22232     },
22233
22234     // private
22235     initTrigger : function(){
22236         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
22237         this.trigger.addClassOnOver('x-form-trigger-over');
22238         this.trigger.addClassOnClick('x-form-trigger-click');
22239     },
22240
22241     // private
22242     onDestroy : function(){
22243         if(this.trigger){
22244             this.trigger.removeAllListeners();
22245             this.trigger.remove();
22246         }
22247         if(this.wrap){
22248             this.wrap.remove();
22249         }
22250         Roo.form.TriggerField.superclass.onDestroy.call(this);
22251     },
22252
22253     // private
22254     onFocus : function(){
22255         Roo.form.TriggerField.superclass.onFocus.call(this);
22256         if(!this.mimicing){
22257             this.wrap.addClass('x-trigger-wrap-focus');
22258             this.mimicing = true;
22259             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
22260             if(this.monitorTab){
22261                 this.el.on("keydown", this.checkTab, this);
22262             }
22263         }
22264     },
22265
22266     // private
22267     checkTab : function(e){
22268         if(e.getKey() == e.TAB){
22269             this.triggerBlur();
22270         }
22271     },
22272
22273     // private
22274     onBlur : function(){
22275         // do nothing
22276     },
22277
22278     // private
22279     mimicBlur : function(e, t){
22280         if(!this.wrap.contains(t) && this.validateBlur()){
22281             this.triggerBlur();
22282         }
22283     },
22284
22285     // private
22286     triggerBlur : function(){
22287         this.mimicing = false;
22288         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
22289         if(this.monitorTab){
22290             this.el.un("keydown", this.checkTab, this);
22291         }
22292         this.wrap.removeClass('x-trigger-wrap-focus');
22293         Roo.form.TriggerField.superclass.onBlur.call(this);
22294     },
22295
22296     // private
22297     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
22298     validateBlur : function(e, t){
22299         return true;
22300     },
22301
22302     // private
22303     onDisable : function(){
22304         Roo.form.TriggerField.superclass.onDisable.call(this);
22305         if(this.wrap){
22306             this.wrap.addClass('x-item-disabled');
22307         }
22308     },
22309
22310     // private
22311     onEnable : function(){
22312         Roo.form.TriggerField.superclass.onEnable.call(this);
22313         if(this.wrap){
22314             this.wrap.removeClass('x-item-disabled');
22315         }
22316     },
22317
22318     // private
22319     onShow : function(){
22320         var ae = this.getActionEl();
22321         
22322         if(ae){
22323             ae.dom.style.display = '';
22324             ae.dom.style.visibility = 'visible';
22325         }
22326     },
22327
22328     // private
22329     
22330     onHide : function(){
22331         var ae = this.getActionEl();
22332         ae.dom.style.display = 'none';
22333     },
22334
22335     /**
22336      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
22337      * by an implementing function.
22338      * @method
22339      * @param {EventObject} e
22340      */
22341     onTriggerClick : Roo.emptyFn
22342 });
22343
22344 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
22345 // to be extended by an implementing class.  For an example of implementing this class, see the custom
22346 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
22347 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
22348     initComponent : function(){
22349         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
22350
22351         this.triggerConfig = {
22352             tag:'span', cls:'x-form-twin-triggers', cn:[
22353             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
22354             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
22355         ]};
22356     },
22357
22358     getTrigger : function(index){
22359         return this.triggers[index];
22360     },
22361
22362     initTrigger : function(){
22363         var ts = this.trigger.select('.x-form-trigger', true);
22364         this.wrap.setStyle('overflow', 'hidden');
22365         var triggerField = this;
22366         ts.each(function(t, all, index){
22367             t.hide = function(){
22368                 var w = triggerField.wrap.getWidth();
22369                 this.dom.style.display = 'none';
22370                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
22371             };
22372             t.show = function(){
22373                 var w = triggerField.wrap.getWidth();
22374                 this.dom.style.display = '';
22375                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
22376             };
22377             var triggerIndex = 'Trigger'+(index+1);
22378
22379             if(this['hide'+triggerIndex]){
22380                 t.dom.style.display = 'none';
22381             }
22382             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
22383             t.addClassOnOver('x-form-trigger-over');
22384             t.addClassOnClick('x-form-trigger-click');
22385         }, this);
22386         this.triggers = ts.elements;
22387     },
22388
22389     onTrigger1Click : Roo.emptyFn,
22390     onTrigger2Click : Roo.emptyFn
22391 });/*
22392  * Based on:
22393  * Ext JS Library 1.1.1
22394  * Copyright(c) 2006-2007, Ext JS, LLC.
22395  *
22396  * Originally Released Under LGPL - original licence link has changed is not relivant.
22397  *
22398  * Fork - LGPL
22399  * <script type="text/javascript">
22400  */
22401  
22402 /**
22403  * @class Roo.form.TextArea
22404  * @extends Roo.form.TextField
22405  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
22406  * support for auto-sizing.
22407  * @constructor
22408  * Creates a new TextArea
22409  * @param {Object} config Configuration options
22410  */
22411 Roo.form.TextArea = function(config){
22412     Roo.form.TextArea.superclass.constructor.call(this, config);
22413     // these are provided exchanges for backwards compat
22414     // minHeight/maxHeight were replaced by growMin/growMax to be
22415     // compatible with TextField growing config values
22416     if(this.minHeight !== undefined){
22417         this.growMin = this.minHeight;
22418     }
22419     if(this.maxHeight !== undefined){
22420         this.growMax = this.maxHeight;
22421     }
22422 };
22423
22424 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
22425     /**
22426      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
22427      */
22428     growMin : 60,
22429     /**
22430      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
22431      */
22432     growMax: 1000,
22433     /**
22434      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
22435      * in the field (equivalent to setting overflow: hidden, defaults to false)
22436      */
22437     preventScrollbars: false,
22438     /**
22439      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
22440      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
22441      */
22442
22443     // private
22444     onRender : function(ct, position){
22445         if(!this.el){
22446             this.defaultAutoCreate = {
22447                 tag: "textarea",
22448                 style:"width:300px;height:60px;",
22449                 autocomplete: "off"
22450             };
22451         }
22452         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
22453         if(this.grow){
22454             this.textSizeEl = Roo.DomHelper.append(document.body, {
22455                 tag: "pre", cls: "x-form-grow-sizer"
22456             });
22457             if(this.preventScrollbars){
22458                 this.el.setStyle("overflow", "hidden");
22459             }
22460             this.el.setHeight(this.growMin);
22461         }
22462     },
22463
22464     onDestroy : function(){
22465         if(this.textSizeEl){
22466             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
22467         }
22468         Roo.form.TextArea.superclass.onDestroy.call(this);
22469     },
22470
22471     // private
22472     onKeyUp : function(e){
22473         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
22474             this.autoSize();
22475         }
22476     },
22477
22478     /**
22479      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
22480      * This only takes effect if grow = true, and fires the autosize event if the height changes.
22481      */
22482     autoSize : function(){
22483         if(!this.grow || !this.textSizeEl){
22484             return;
22485         }
22486         var el = this.el;
22487         var v = el.dom.value;
22488         var ts = this.textSizeEl;
22489
22490         ts.innerHTML = '';
22491         ts.appendChild(document.createTextNode(v));
22492         v = ts.innerHTML;
22493
22494         Roo.fly(ts).setWidth(this.el.getWidth());
22495         if(v.length < 1){
22496             v = "&#160;&#160;";
22497         }else{
22498             if(Roo.isIE){
22499                 v = v.replace(/\n/g, '<p>&#160;</p>');
22500             }
22501             v += "&#160;\n&#160;";
22502         }
22503         ts.innerHTML = v;
22504         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
22505         if(h != this.lastHeight){
22506             this.lastHeight = h;
22507             this.el.setHeight(h);
22508             this.fireEvent("autosize", this, h);
22509         }
22510     }
22511 });/*
22512  * Based on:
22513  * Ext JS Library 1.1.1
22514  * Copyright(c) 2006-2007, Ext JS, LLC.
22515  *
22516  * Originally Released Under LGPL - original licence link has changed is not relivant.
22517  *
22518  * Fork - LGPL
22519  * <script type="text/javascript">
22520  */
22521  
22522
22523 /**
22524  * @class Roo.form.NumberField
22525  * @extends Roo.form.TextField
22526  * Numeric text field that provides automatic keystroke filtering and numeric validation.
22527  * @constructor
22528  * Creates a new NumberField
22529  * @param {Object} config Configuration options
22530  */
22531 Roo.form.NumberField = function(config){
22532     Roo.form.NumberField.superclass.constructor.call(this, config);
22533 };
22534
22535 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
22536     /**
22537      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
22538      */
22539     fieldClass: "x-form-field x-form-num-field",
22540     /**
22541      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
22542      */
22543     allowDecimals : true,
22544     /**
22545      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
22546      */
22547     decimalSeparator : ".",
22548     /**
22549      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
22550      */
22551     decimalPrecision : 2,
22552     /**
22553      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
22554      */
22555     allowNegative : true,
22556     /**
22557      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
22558      */
22559     minValue : Number.NEGATIVE_INFINITY,
22560     /**
22561      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
22562      */
22563     maxValue : Number.MAX_VALUE,
22564     /**
22565      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
22566      */
22567     minText : "The minimum value for this field is {0}",
22568     /**
22569      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
22570      */
22571     maxText : "The maximum value for this field is {0}",
22572     /**
22573      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
22574      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
22575      */
22576     nanText : "{0} is not a valid number",
22577
22578     // private
22579     initEvents : function(){
22580         Roo.form.NumberField.superclass.initEvents.call(this);
22581         var allowed = "0123456789";
22582         if(this.allowDecimals){
22583             allowed += this.decimalSeparator;
22584         }
22585         if(this.allowNegative){
22586             allowed += "-";
22587         }
22588         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
22589         var keyPress = function(e){
22590             var k = e.getKey();
22591             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
22592                 return;
22593             }
22594             var c = e.getCharCode();
22595             if(allowed.indexOf(String.fromCharCode(c)) === -1){
22596                 e.stopEvent();
22597             }
22598         };
22599         this.el.on("keypress", keyPress, this);
22600     },
22601
22602     // private
22603     validateValue : function(value){
22604         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
22605             return false;
22606         }
22607         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22608              return true;
22609         }
22610         var num = this.parseValue(value);
22611         if(isNaN(num)){
22612             this.markInvalid(String.format(this.nanText, value));
22613             return false;
22614         }
22615         if(num < this.minValue){
22616             this.markInvalid(String.format(this.minText, this.minValue));
22617             return false;
22618         }
22619         if(num > this.maxValue){
22620             this.markInvalid(String.format(this.maxText, this.maxValue));
22621             return false;
22622         }
22623         return true;
22624     },
22625
22626     getValue : function(){
22627         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
22628     },
22629
22630     // private
22631     parseValue : function(value){
22632         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
22633         return isNaN(value) ? '' : value;
22634     },
22635
22636     // private
22637     fixPrecision : function(value){
22638         var nan = isNaN(value);
22639         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
22640             return nan ? '' : value;
22641         }
22642         return parseFloat(value).toFixed(this.decimalPrecision);
22643     },
22644
22645     setValue : function(v){
22646         v = this.fixPrecision(v);
22647         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
22648     },
22649
22650     // private
22651     decimalPrecisionFcn : function(v){
22652         return Math.floor(v);
22653     },
22654
22655     beforeBlur : function(){
22656         var v = this.parseValue(this.getRawValue());
22657         if(v){
22658             this.setValue(v);
22659         }
22660     }
22661 });/*
22662  * Based on:
22663  * Ext JS Library 1.1.1
22664  * Copyright(c) 2006-2007, Ext JS, LLC.
22665  *
22666  * Originally Released Under LGPL - original licence link has changed is not relivant.
22667  *
22668  * Fork - LGPL
22669  * <script type="text/javascript">
22670  */
22671  
22672 /**
22673  * @class Roo.form.DateField
22674  * @extends Roo.form.TriggerField
22675  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22676 * @constructor
22677 * Create a new DateField
22678 * @param {Object} config
22679  */
22680 Roo.form.DateField = function(config){
22681     Roo.form.DateField.superclass.constructor.call(this, config);
22682     
22683       this.addEvents({
22684          
22685         /**
22686          * @event select
22687          * Fires when a date is selected
22688              * @param {Roo.form.DateField} combo This combo box
22689              * @param {Date} date The date selected
22690              */
22691         'select' : true
22692          
22693     });
22694     
22695     
22696     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22697     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22698     this.ddMatch = null;
22699     if(this.disabledDates){
22700         var dd = this.disabledDates;
22701         var re = "(?:";
22702         for(var i = 0; i < dd.length; i++){
22703             re += dd[i];
22704             if(i != dd.length-1) re += "|";
22705         }
22706         this.ddMatch = new RegExp(re + ")");
22707     }
22708 };
22709
22710 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
22711     /**
22712      * @cfg {String} format
22713      * The default date format string which can be overriden for localization support.  The format must be
22714      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22715      */
22716     format : "m/d/y",
22717     /**
22718      * @cfg {String} altFormats
22719      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22720      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22721      */
22722     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22723     /**
22724      * @cfg {Array} disabledDays
22725      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22726      */
22727     disabledDays : null,
22728     /**
22729      * @cfg {String} disabledDaysText
22730      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22731      */
22732     disabledDaysText : "Disabled",
22733     /**
22734      * @cfg {Array} disabledDates
22735      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22736      * expression so they are very powerful. Some examples:
22737      * <ul>
22738      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22739      * <li>["03/08", "09/16"] would disable those days for every year</li>
22740      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22741      * <li>["03/../2006"] would disable every day in March 2006</li>
22742      * <li>["^03"] would disable every day in every March</li>
22743      * </ul>
22744      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22745      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22746      */
22747     disabledDates : null,
22748     /**
22749      * @cfg {String} disabledDatesText
22750      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22751      */
22752     disabledDatesText : "Disabled",
22753     /**
22754      * @cfg {Date/String} minValue
22755      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22756      * valid format (defaults to null).
22757      */
22758     minValue : null,
22759     /**
22760      * @cfg {Date/String} maxValue
22761      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22762      * valid format (defaults to null).
22763      */
22764     maxValue : null,
22765     /**
22766      * @cfg {String} minText
22767      * The error text to display when the date in the cell is before minValue (defaults to
22768      * 'The date in this field must be after {minValue}').
22769      */
22770     minText : "The date in this field must be equal to or after {0}",
22771     /**
22772      * @cfg {String} maxText
22773      * The error text to display when the date in the cell is after maxValue (defaults to
22774      * 'The date in this field must be before {maxValue}').
22775      */
22776     maxText : "The date in this field must be equal to or before {0}",
22777     /**
22778      * @cfg {String} invalidText
22779      * The error text to display when the date in the field is invalid (defaults to
22780      * '{value} is not a valid date - it must be in the format {format}').
22781      */
22782     invalidText : "{0} is not a valid date - it must be in the format {1}",
22783     /**
22784      * @cfg {String} triggerClass
22785      * An additional CSS class used to style the trigger button.  The trigger will always get the
22786      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22787      * which displays a calendar icon).
22788      */
22789     triggerClass : 'x-form-date-trigger',
22790     
22791
22792     /**
22793      * @cfg {Boolean} useIso
22794      * if enabled, then the date field will use a hidden field to store the 
22795      * real value as iso formated date. default (false)
22796      */ 
22797     useIso : false,
22798     /**
22799      * @cfg {String/Object} autoCreate
22800      * A DomHelper element spec, or true for a default element spec (defaults to
22801      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22802      */ 
22803     // private
22804     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22805     
22806     // private
22807     hiddenField: false,
22808     
22809     onRender : function(ct, position)
22810     {
22811         Roo.form.DateField.superclass.onRender.call(this, ct, position);
22812         if (this.useIso) {
22813             //this.el.dom.removeAttribute('name'); 
22814             Roo.log("Changing name?");
22815             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
22816             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22817                     'before', true);
22818             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
22819             // prevent input submission
22820             this.hiddenName = this.name;
22821         }
22822             
22823             
22824     },
22825     
22826     // private
22827     validateValue : function(value)
22828     {
22829         value = this.formatDate(value);
22830         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22831             Roo.log('super failed');
22832             return false;
22833         }
22834         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22835              return true;
22836         }
22837         var svalue = value;
22838         value = this.parseDate(value);
22839         if(!value){
22840             Roo.log('parse date failed' + svalue);
22841             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22842             return false;
22843         }
22844         var time = value.getTime();
22845         if(this.minValue && time < this.minValue.getTime()){
22846             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22847             return false;
22848         }
22849         if(this.maxValue && time > this.maxValue.getTime()){
22850             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22851             return false;
22852         }
22853         if(this.disabledDays){
22854             var day = value.getDay();
22855             for(var i = 0; i < this.disabledDays.length; i++) {
22856                 if(day === this.disabledDays[i]){
22857                     this.markInvalid(this.disabledDaysText);
22858                     return false;
22859                 }
22860             }
22861         }
22862         var fvalue = this.formatDate(value);
22863         if(this.ddMatch && this.ddMatch.test(fvalue)){
22864             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22865             return false;
22866         }
22867         return true;
22868     },
22869
22870     // private
22871     // Provides logic to override the default TriggerField.validateBlur which just returns true
22872     validateBlur : function(){
22873         return !this.menu || !this.menu.isVisible();
22874     },
22875     
22876     getName: function()
22877     {
22878         // returns hidden if it's set..
22879         if (!this.rendered) {return ''};
22880         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
22881         
22882     },
22883
22884     /**
22885      * Returns the current date value of the date field.
22886      * @return {Date} The date value
22887      */
22888     getValue : function(){
22889         
22890         return  this.hiddenField ?
22891                 this.hiddenField.value :
22892                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22893     },
22894
22895     /**
22896      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22897      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22898      * (the default format used is "m/d/y").
22899      * <br />Usage:
22900      * <pre><code>
22901 //All of these calls set the same date value (May 4, 2006)
22902
22903 //Pass a date object:
22904 var dt = new Date('5/4/06');
22905 dateField.setValue(dt);
22906
22907 //Pass a date string (default format):
22908 dateField.setValue('5/4/06');
22909
22910 //Pass a date string (custom format):
22911 dateField.format = 'Y-m-d';
22912 dateField.setValue('2006-5-4');
22913 </code></pre>
22914      * @param {String/Date} date The date or valid date string
22915      */
22916     setValue : function(date){
22917         if (this.hiddenField) {
22918             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22919         }
22920         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22921         // make sure the value field is always stored as a date..
22922         this.value = this.parseDate(date);
22923         
22924         
22925     },
22926
22927     // private
22928     parseDate : function(value){
22929         if(!value || value instanceof Date){
22930             return value;
22931         }
22932         var v = Date.parseDate(value, this.format);
22933          if (!v && this.useIso) {
22934             v = Date.parseDate(value, 'Y-m-d');
22935         }
22936         if(!v && this.altFormats){
22937             if(!this.altFormatsArray){
22938                 this.altFormatsArray = this.altFormats.split("|");
22939             }
22940             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22941                 v = Date.parseDate(value, this.altFormatsArray[i]);
22942             }
22943         }
22944         return v;
22945     },
22946
22947     // private
22948     formatDate : function(date, fmt){
22949         return (!date || !(date instanceof Date)) ?
22950                date : date.dateFormat(fmt || this.format);
22951     },
22952
22953     // private
22954     menuListeners : {
22955         select: function(m, d){
22956             
22957             this.setValue(d);
22958             this.fireEvent('select', this, d);
22959         },
22960         show : function(){ // retain focus styling
22961             this.onFocus();
22962         },
22963         hide : function(){
22964             this.focus.defer(10, this);
22965             var ml = this.menuListeners;
22966             this.menu.un("select", ml.select,  this);
22967             this.menu.un("show", ml.show,  this);
22968             this.menu.un("hide", ml.hide,  this);
22969         }
22970     },
22971
22972     // private
22973     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22974     onTriggerClick : function(){
22975         if(this.disabled){
22976             return;
22977         }
22978         if(this.menu == null){
22979             this.menu = new Roo.menu.DateMenu();
22980         }
22981         Roo.apply(this.menu.picker,  {
22982             showClear: this.allowBlank,
22983             minDate : this.minValue,
22984             maxDate : this.maxValue,
22985             disabledDatesRE : this.ddMatch,
22986             disabledDatesText : this.disabledDatesText,
22987             disabledDays : this.disabledDays,
22988             disabledDaysText : this.disabledDaysText,
22989             format : this.useIso ? 'Y-m-d' : this.format,
22990             minText : String.format(this.minText, this.formatDate(this.minValue)),
22991             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22992         });
22993         this.menu.on(Roo.apply({}, this.menuListeners, {
22994             scope:this
22995         }));
22996         this.menu.picker.setValue(this.getValue() || new Date());
22997         this.menu.show(this.el, "tl-bl?");
22998     },
22999
23000     beforeBlur : function(){
23001         var v = this.parseDate(this.getRawValue());
23002         if(v){
23003             this.setValue(v);
23004         }
23005     }
23006
23007     /** @cfg {Boolean} grow @hide */
23008     /** @cfg {Number} growMin @hide */
23009     /** @cfg {Number} growMax @hide */
23010     /**
23011      * @hide
23012      * @method autoSize
23013      */
23014 });/*
23015  * Based on:
23016  * Ext JS Library 1.1.1
23017  * Copyright(c) 2006-2007, Ext JS, LLC.
23018  *
23019  * Originally Released Under LGPL - original licence link has changed is not relivant.
23020  *
23021  * Fork - LGPL
23022  * <script type="text/javascript">
23023  */
23024  
23025 /**
23026  * @class Roo.form.MonthField
23027  * @extends Roo.form.TriggerField
23028  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
23029 * @constructor
23030 * Create a new MonthField
23031 * @param {Object} config
23032  */
23033 Roo.form.MonthField = function(config){
23034     
23035     Roo.form.MonthField.superclass.constructor.call(this, config);
23036     
23037       this.addEvents({
23038          
23039         /**
23040          * @event select
23041          * Fires when a date is selected
23042              * @param {Roo.form.MonthFieeld} combo This combo box
23043              * @param {Date} date The date selected
23044              */
23045         'select' : true
23046          
23047     });
23048     
23049     
23050     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
23051     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
23052     this.ddMatch = null;
23053     if(this.disabledDates){
23054         var dd = this.disabledDates;
23055         var re = "(?:";
23056         for(var i = 0; i < dd.length; i++){
23057             re += dd[i];
23058             if(i != dd.length-1) re += "|";
23059         }
23060         this.ddMatch = new RegExp(re + ")");
23061     }
23062 };
23063
23064 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
23065     /**
23066      * @cfg {String} format
23067      * The default date format string which can be overriden for localization support.  The format must be
23068      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
23069      */
23070     format : "M Y",
23071     /**
23072      * @cfg {String} altFormats
23073      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
23074      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
23075      */
23076     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
23077     /**
23078      * @cfg {Array} disabledDays
23079      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
23080      */
23081     disabledDays : [0,1,2,3,4,5,6],
23082     /**
23083      * @cfg {String} disabledDaysText
23084      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
23085      */
23086     disabledDaysText : "Disabled",
23087     /**
23088      * @cfg {Array} disabledDates
23089      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
23090      * expression so they are very powerful. Some examples:
23091      * <ul>
23092      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
23093      * <li>["03/08", "09/16"] would disable those days for every year</li>
23094      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
23095      * <li>["03/../2006"] would disable every day in March 2006</li>
23096      * <li>["^03"] would disable every day in every March</li>
23097      * </ul>
23098      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
23099      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
23100      */
23101     disabledDates : null,
23102     /**
23103      * @cfg {String} disabledDatesText
23104      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
23105      */
23106     disabledDatesText : "Disabled",
23107     /**
23108      * @cfg {Date/String} minValue
23109      * The minimum allowed date. Can be either a Javascript date object or a string date in a
23110      * valid format (defaults to null).
23111      */
23112     minValue : null,
23113     /**
23114      * @cfg {Date/String} maxValue
23115      * The maximum allowed date. Can be either a Javascript date object or a string date in a
23116      * valid format (defaults to null).
23117      */
23118     maxValue : null,
23119     /**
23120      * @cfg {String} minText
23121      * The error text to display when the date in the cell is before minValue (defaults to
23122      * 'The date in this field must be after {minValue}').
23123      */
23124     minText : "The date in this field must be equal to or after {0}",
23125     /**
23126      * @cfg {String} maxTextf
23127      * The error text to display when the date in the cell is after maxValue (defaults to
23128      * 'The date in this field must be before {maxValue}').
23129      */
23130     maxText : "The date in this field must be equal to or before {0}",
23131     /**
23132      * @cfg {String} invalidText
23133      * The error text to display when the date in the field is invalid (defaults to
23134      * '{value} is not a valid date - it must be in the format {format}').
23135      */
23136     invalidText : "{0} is not a valid date - it must be in the format {1}",
23137     /**
23138      * @cfg {String} triggerClass
23139      * An additional CSS class used to style the trigger button.  The trigger will always get the
23140      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
23141      * which displays a calendar icon).
23142      */
23143     triggerClass : 'x-form-date-trigger',
23144     
23145
23146     /**
23147      * @cfg {Boolean} useIso
23148      * if enabled, then the date field will use a hidden field to store the 
23149      * real value as iso formated date. default (true)
23150      */ 
23151     useIso : true,
23152     /**
23153      * @cfg {String/Object} autoCreate
23154      * A DomHelper element spec, or true for a default element spec (defaults to
23155      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
23156      */ 
23157     // private
23158     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
23159     
23160     // private
23161     hiddenField: false,
23162     
23163     hideMonthPicker : false,
23164     
23165     onRender : function(ct, position)
23166     {
23167         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
23168         if (this.useIso) {
23169             this.el.dom.removeAttribute('name'); 
23170             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
23171                     'before', true);
23172             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
23173             // prevent input submission
23174             this.hiddenName = this.name;
23175         }
23176             
23177             
23178     },
23179     
23180     // private
23181     validateValue : function(value)
23182     {
23183         value = this.formatDate(value);
23184         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
23185             return false;
23186         }
23187         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
23188              return true;
23189         }
23190         var svalue = value;
23191         value = this.parseDate(value);
23192         if(!value){
23193             this.markInvalid(String.format(this.invalidText, svalue, this.format));
23194             return false;
23195         }
23196         var time = value.getTime();
23197         if(this.minValue && time < this.minValue.getTime()){
23198             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
23199             return false;
23200         }
23201         if(this.maxValue && time > this.maxValue.getTime()){
23202             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
23203             return false;
23204         }
23205         /*if(this.disabledDays){
23206             var day = value.getDay();
23207             for(var i = 0; i < this.disabledDays.length; i++) {
23208                 if(day === this.disabledDays[i]){
23209                     this.markInvalid(this.disabledDaysText);
23210                     return false;
23211                 }
23212             }
23213         }
23214         */
23215         var fvalue = this.formatDate(value);
23216         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
23217             this.markInvalid(String.format(this.disabledDatesText, fvalue));
23218             return false;
23219         }
23220         */
23221         return true;
23222     },
23223
23224     // private
23225     // Provides logic to override the default TriggerField.validateBlur which just returns true
23226     validateBlur : function(){
23227         return !this.menu || !this.menu.isVisible();
23228     },
23229
23230     /**
23231      * Returns the current date value of the date field.
23232      * @return {Date} The date value
23233      */
23234     getValue : function(){
23235         
23236         
23237         
23238         return  this.hiddenField ?
23239                 this.hiddenField.value :
23240                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
23241     },
23242
23243     /**
23244      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
23245      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
23246      * (the default format used is "m/d/y").
23247      * <br />Usage:
23248      * <pre><code>
23249 //All of these calls set the same date value (May 4, 2006)
23250
23251 //Pass a date object:
23252 var dt = new Date('5/4/06');
23253 monthField.setValue(dt);
23254
23255 //Pass a date string (default format):
23256 monthField.setValue('5/4/06');
23257
23258 //Pass a date string (custom format):
23259 monthField.format = 'Y-m-d';
23260 monthField.setValue('2006-5-4');
23261 </code></pre>
23262      * @param {String/Date} date The date or valid date string
23263      */
23264     setValue : function(date){
23265         Roo.log('month setValue' + date);
23266         // can only be first of month..
23267         
23268         var val = this.parseDate(date);
23269         
23270         if (this.hiddenField) {
23271             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
23272         }
23273         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
23274         this.value = this.parseDate(date);
23275     },
23276
23277     // private
23278     parseDate : function(value){
23279         if(!value || value instanceof Date){
23280             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
23281             return value;
23282         }
23283         var v = Date.parseDate(value, this.format);
23284         if (!v && this.useIso) {
23285             v = Date.parseDate(value, 'Y-m-d');
23286         }
23287         if (v) {
23288             // 
23289             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
23290         }
23291         
23292         
23293         if(!v && this.altFormats){
23294             if(!this.altFormatsArray){
23295                 this.altFormatsArray = this.altFormats.split("|");
23296             }
23297             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23298                 v = Date.parseDate(value, this.altFormatsArray[i]);
23299             }
23300         }
23301         return v;
23302     },
23303
23304     // private
23305     formatDate : function(date, fmt){
23306         return (!date || !(date instanceof Date)) ?
23307                date : date.dateFormat(fmt || this.format);
23308     },
23309
23310     // private
23311     menuListeners : {
23312         select: function(m, d){
23313             this.setValue(d);
23314             this.fireEvent('select', this, d);
23315         },
23316         show : function(){ // retain focus styling
23317             this.onFocus();
23318         },
23319         hide : function(){
23320             this.focus.defer(10, this);
23321             var ml = this.menuListeners;
23322             this.menu.un("select", ml.select,  this);
23323             this.menu.un("show", ml.show,  this);
23324             this.menu.un("hide", ml.hide,  this);
23325         }
23326     },
23327     // private
23328     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
23329     onTriggerClick : function(){
23330         if(this.disabled){
23331             return;
23332         }
23333         if(this.menu == null){
23334             this.menu = new Roo.menu.DateMenu();
23335            
23336         }
23337         
23338         Roo.apply(this.menu.picker,  {
23339             
23340             showClear: this.allowBlank,
23341             minDate : this.minValue,
23342             maxDate : this.maxValue,
23343             disabledDatesRE : this.ddMatch,
23344             disabledDatesText : this.disabledDatesText,
23345             
23346             format : this.useIso ? 'Y-m-d' : this.format,
23347             minText : String.format(this.minText, this.formatDate(this.minValue)),
23348             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
23349             
23350         });
23351          this.menu.on(Roo.apply({}, this.menuListeners, {
23352             scope:this
23353         }));
23354        
23355         
23356         var m = this.menu;
23357         var p = m.picker;
23358         
23359         // hide month picker get's called when we called by 'before hide';
23360         
23361         var ignorehide = true;
23362         p.hideMonthPicker  = function(disableAnim){
23363             if (ignorehide) {
23364                 return;
23365             }
23366              if(this.monthPicker){
23367                 Roo.log("hideMonthPicker called");
23368                 if(disableAnim === true){
23369                     this.monthPicker.hide();
23370                 }else{
23371                     this.monthPicker.slideOut('t', {duration:.2});
23372                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
23373                     p.fireEvent("select", this, this.value);
23374                     m.hide();
23375                 }
23376             }
23377         }
23378         
23379         Roo.log('picker set value');
23380         Roo.log(this.getValue());
23381         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
23382         m.show(this.el, 'tl-bl?');
23383         ignorehide  = false;
23384         // this will trigger hideMonthPicker..
23385         
23386         
23387         // hidden the day picker
23388         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
23389         
23390         
23391         
23392       
23393         
23394         p.showMonthPicker.defer(100, p);
23395     
23396         
23397        
23398     },
23399
23400     beforeBlur : function(){
23401         var v = this.parseDate(this.getRawValue());
23402         if(v){
23403             this.setValue(v);
23404         }
23405     }
23406
23407     /** @cfg {Boolean} grow @hide */
23408     /** @cfg {Number} growMin @hide */
23409     /** @cfg {Number} growMax @hide */
23410     /**
23411      * @hide
23412      * @method autoSize
23413      */
23414 });/*
23415  * Based on:
23416  * Ext JS Library 1.1.1
23417  * Copyright(c) 2006-2007, Ext JS, LLC.
23418  *
23419  * Originally Released Under LGPL - original licence link has changed is not relivant.
23420  *
23421  * Fork - LGPL
23422  * <script type="text/javascript">
23423  */
23424  
23425
23426 /**
23427  * @class Roo.form.ComboBox
23428  * @extends Roo.form.TriggerField
23429  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
23430  * @constructor
23431  * Create a new ComboBox.
23432  * @param {Object} config Configuration options
23433  */
23434 Roo.form.ComboBox = function(config){
23435     Roo.form.ComboBox.superclass.constructor.call(this, config);
23436     this.addEvents({
23437         /**
23438          * @event expand
23439          * Fires when the dropdown list is expanded
23440              * @param {Roo.form.ComboBox} combo This combo box
23441              */
23442         'expand' : true,
23443         /**
23444          * @event collapse
23445          * Fires when the dropdown list is collapsed
23446              * @param {Roo.form.ComboBox} combo This combo box
23447              */
23448         'collapse' : true,
23449         /**
23450          * @event beforeselect
23451          * Fires before a list item is selected. Return false to cancel the selection.
23452              * @param {Roo.form.ComboBox} combo This combo box
23453              * @param {Roo.data.Record} record The data record returned from the underlying store
23454              * @param {Number} index The index of the selected item in the dropdown list
23455              */
23456         'beforeselect' : true,
23457         /**
23458          * @event select
23459          * Fires when a list item is selected
23460              * @param {Roo.form.ComboBox} combo This combo box
23461              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
23462              * @param {Number} index The index of the selected item in the dropdown list
23463              */
23464         'select' : true,
23465         /**
23466          * @event beforequery
23467          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
23468          * The event object passed has these properties:
23469              * @param {Roo.form.ComboBox} combo This combo box
23470              * @param {String} query The query
23471              * @param {Boolean} forceAll true to force "all" query
23472              * @param {Boolean} cancel true to cancel the query
23473              * @param {Object} e The query event object
23474              */
23475         'beforequery': true,
23476          /**
23477          * @event add
23478          * Fires when the 'add' icon is pressed (add a listener to enable add button)
23479              * @param {Roo.form.ComboBox} combo This combo box
23480              */
23481         'add' : true,
23482         /**
23483          * @event edit
23484          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
23485              * @param {Roo.form.ComboBox} combo This combo box
23486              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
23487              */
23488         'edit' : true
23489         
23490         
23491     });
23492     if(this.transform){
23493         this.allowDomMove = false;
23494         var s = Roo.getDom(this.transform);
23495         if(!this.hiddenName){
23496             this.hiddenName = s.name;
23497         }
23498         if(!this.store){
23499             this.mode = 'local';
23500             var d = [], opts = s.options;
23501             for(var i = 0, len = opts.length;i < len; i++){
23502                 var o = opts[i];
23503                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
23504                 if(o.selected) {
23505                     this.value = value;
23506                 }
23507                 d.push([value, o.text]);
23508             }
23509             this.store = new Roo.data.SimpleStore({
23510                 'id': 0,
23511                 fields: ['value', 'text'],
23512                 data : d
23513             });
23514             this.valueField = 'value';
23515             this.displayField = 'text';
23516         }
23517         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
23518         if(!this.lazyRender){
23519             this.target = true;
23520             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
23521             s.parentNode.removeChild(s); // remove it
23522             this.render(this.el.parentNode);
23523         }else{
23524             s.parentNode.removeChild(s); // remove it
23525         }
23526
23527     }
23528     if (this.store) {
23529         this.store = Roo.factory(this.store, Roo.data);
23530     }
23531     
23532     this.selectedIndex = -1;
23533     if(this.mode == 'local'){
23534         if(config.queryDelay === undefined){
23535             this.queryDelay = 10;
23536         }
23537         if(config.minChars === undefined){
23538             this.minChars = 0;
23539         }
23540     }
23541 };
23542
23543 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
23544     /**
23545      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
23546      */
23547     /**
23548      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
23549      * rendering into an Roo.Editor, defaults to false)
23550      */
23551     /**
23552      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
23553      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
23554      */
23555     /**
23556      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
23557      */
23558     /**
23559      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
23560      * the dropdown list (defaults to undefined, with no header element)
23561      */
23562
23563      /**
23564      * @cfg {String/Roo.Template} tpl The template to use to render the output
23565      */
23566      
23567     // private
23568     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
23569     /**
23570      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
23571      */
23572     listWidth: undefined,
23573     /**
23574      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
23575      * mode = 'remote' or 'text' if mode = 'local')
23576      */
23577     displayField: undefined,
23578     /**
23579      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
23580      * mode = 'remote' or 'value' if mode = 'local'). 
23581      * Note: use of a valueField requires the user make a selection
23582      * in order for a value to be mapped.
23583      */
23584     valueField: undefined,
23585     
23586     
23587     /**
23588      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
23589      * field's data value (defaults to the underlying DOM element's name)
23590      */
23591     hiddenName: undefined,
23592     /**
23593      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
23594      */
23595     listClass: '',
23596     /**
23597      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
23598      */
23599     selectedClass: 'x-combo-selected',
23600     /**
23601      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
23602      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
23603      * which displays a downward arrow icon).
23604      */
23605     triggerClass : 'x-form-arrow-trigger',
23606     /**
23607      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
23608      */
23609     shadow:'sides',
23610     /**
23611      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
23612      * anchor positions (defaults to 'tl-bl')
23613      */
23614     listAlign: 'tl-bl?',
23615     /**
23616      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
23617      */
23618     maxHeight: 300,
23619     /**
23620      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
23621      * query specified by the allQuery config option (defaults to 'query')
23622      */
23623     triggerAction: 'query',
23624     /**
23625      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
23626      * (defaults to 4, does not apply if editable = false)
23627      */
23628     minChars : 4,
23629     /**
23630      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
23631      * delay (typeAheadDelay) if it matches a known value (defaults to false)
23632      */
23633     typeAhead: false,
23634     /**
23635      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
23636      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
23637      */
23638     queryDelay: 500,
23639     /**
23640      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
23641      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
23642      */
23643     pageSize: 0,
23644     /**
23645      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
23646      * when editable = true (defaults to false)
23647      */
23648     selectOnFocus:false,
23649     /**
23650      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
23651      */
23652     queryParam: 'query',
23653     /**
23654      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
23655      * when mode = 'remote' (defaults to 'Loading...')
23656      */
23657     loadingText: 'Loading...',
23658     /**
23659      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
23660      */
23661     resizable: false,
23662     /**
23663      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
23664      */
23665     handleHeight : 8,
23666     /**
23667      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
23668      * traditional select (defaults to true)
23669      */
23670     editable: true,
23671     /**
23672      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
23673      */
23674     allQuery: '',
23675     /**
23676      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
23677      */
23678     mode: 'remote',
23679     /**
23680      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
23681      * listWidth has a higher value)
23682      */
23683     minListWidth : 70,
23684     /**
23685      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
23686      * allow the user to set arbitrary text into the field (defaults to false)
23687      */
23688     forceSelection:false,
23689     /**
23690      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
23691      * if typeAhead = true (defaults to 250)
23692      */
23693     typeAheadDelay : 250,
23694     /**
23695      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
23696      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
23697      */
23698     valueNotFoundText : undefined,
23699     /**
23700      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
23701      */
23702     blockFocus : false,
23703     
23704     /**
23705      * @cfg {Boolean} disableClear Disable showing of clear button.
23706      */
23707     disableClear : false,
23708     /**
23709      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
23710      */
23711     alwaysQuery : false,
23712     
23713     //private
23714     addicon : false,
23715     editicon: false,
23716     
23717     // element that contains real text value.. (when hidden is used..)
23718      
23719     // private
23720     onRender : function(ct, position){
23721         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
23722         if(this.hiddenName){
23723             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
23724                     'before', true);
23725             this.hiddenField.value =
23726                 this.hiddenValue !== undefined ? this.hiddenValue :
23727                 this.value !== undefined ? this.value : '';
23728
23729             // prevent input submission
23730             this.el.dom.removeAttribute('name');
23731              
23732              
23733         }
23734         if(Roo.isGecko){
23735             this.el.dom.setAttribute('autocomplete', 'off');
23736         }
23737
23738         var cls = 'x-combo-list';
23739
23740         this.list = new Roo.Layer({
23741             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
23742         });
23743
23744         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
23745         this.list.setWidth(lw);
23746         this.list.swallowEvent('mousewheel');
23747         this.assetHeight = 0;
23748
23749         if(this.title){
23750             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
23751             this.assetHeight += this.header.getHeight();
23752         }
23753
23754         this.innerList = this.list.createChild({cls:cls+'-inner'});
23755         this.innerList.on('mouseover', this.onViewOver, this);
23756         this.innerList.on('mousemove', this.onViewMove, this);
23757         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23758         
23759         if(this.allowBlank && !this.pageSize && !this.disableClear){
23760             this.footer = this.list.createChild({cls:cls+'-ft'});
23761             this.pageTb = new Roo.Toolbar(this.footer);
23762            
23763         }
23764         if(this.pageSize){
23765             this.footer = this.list.createChild({cls:cls+'-ft'});
23766             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
23767                     {pageSize: this.pageSize});
23768             
23769         }
23770         
23771         if (this.pageTb && this.allowBlank && !this.disableClear) {
23772             var _this = this;
23773             this.pageTb.add(new Roo.Toolbar.Fill(), {
23774                 cls: 'x-btn-icon x-btn-clear',
23775                 text: '&#160;',
23776                 handler: function()
23777                 {
23778                     _this.collapse();
23779                     _this.clearValue();
23780                     _this.onSelect(false, -1);
23781                 }
23782             });
23783         }
23784         if (this.footer) {
23785             this.assetHeight += this.footer.getHeight();
23786         }
23787         
23788
23789         if(!this.tpl){
23790             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
23791         }
23792
23793         this.view = new Roo.View(this.innerList, this.tpl, {
23794             singleSelect:true, store: this.store, selectedClass: this.selectedClass
23795         });
23796
23797         this.view.on('click', this.onViewClick, this);
23798
23799         this.store.on('beforeload', this.onBeforeLoad, this);
23800         this.store.on('load', this.onLoad, this);
23801         this.store.on('loadexception', this.onLoadException, this);
23802
23803         if(this.resizable){
23804             this.resizer = new Roo.Resizable(this.list,  {
23805                pinned:true, handles:'se'
23806             });
23807             this.resizer.on('resize', function(r, w, h){
23808                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
23809                 this.listWidth = w;
23810                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
23811                 this.restrictHeight();
23812             }, this);
23813             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
23814         }
23815         if(!this.editable){
23816             this.editable = true;
23817             this.setEditable(false);
23818         }  
23819         
23820         
23821         if (typeof(this.events.add.listeners) != 'undefined') {
23822             
23823             this.addicon = this.wrap.createChild(
23824                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
23825        
23826             this.addicon.on('click', function(e) {
23827                 this.fireEvent('add', this);
23828             }, this);
23829         }
23830         if (typeof(this.events.edit.listeners) != 'undefined') {
23831             
23832             this.editicon = this.wrap.createChild(
23833                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
23834             if (this.addicon) {
23835                 this.editicon.setStyle('margin-left', '40px');
23836             }
23837             this.editicon.on('click', function(e) {
23838                 
23839                 // we fire even  if inothing is selected..
23840                 this.fireEvent('edit', this, this.lastData );
23841                 
23842             }, this);
23843         }
23844         
23845         
23846         
23847     },
23848
23849     // private
23850     initEvents : function(){
23851         Roo.form.ComboBox.superclass.initEvents.call(this);
23852
23853         this.keyNav = new Roo.KeyNav(this.el, {
23854             "up" : function(e){
23855                 this.inKeyMode = true;
23856                 this.selectPrev();
23857             },
23858
23859             "down" : function(e){
23860                 if(!this.isExpanded()){
23861                     this.onTriggerClick();
23862                 }else{
23863                     this.inKeyMode = true;
23864                     this.selectNext();
23865                 }
23866             },
23867
23868             "enter" : function(e){
23869                 this.onViewClick();
23870                 //return true;
23871             },
23872
23873             "esc" : function(e){
23874                 this.collapse();
23875             },
23876
23877             "tab" : function(e){
23878                 this.onViewClick(false);
23879                 this.fireEvent("specialkey", this, e);
23880                 return true;
23881             },
23882
23883             scope : this,
23884
23885             doRelay : function(foo, bar, hname){
23886                 if(hname == 'down' || this.scope.isExpanded()){
23887                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
23888                 }
23889                 return true;
23890             },
23891
23892             forceKeyDown: true
23893         });
23894         this.queryDelay = Math.max(this.queryDelay || 10,
23895                 this.mode == 'local' ? 10 : 250);
23896         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
23897         if(this.typeAhead){
23898             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
23899         }
23900         if(this.editable !== false){
23901             this.el.on("keyup", this.onKeyUp, this);
23902         }
23903         if(this.forceSelection){
23904             this.on('blur', this.doForce, this);
23905         }
23906     },
23907
23908     onDestroy : function(){
23909         if(this.view){
23910             this.view.setStore(null);
23911             this.view.el.removeAllListeners();
23912             this.view.el.remove();
23913             this.view.purgeListeners();
23914         }
23915         if(this.list){
23916             this.list.destroy();
23917         }
23918         if(this.store){
23919             this.store.un('beforeload', this.onBeforeLoad, this);
23920             this.store.un('load', this.onLoad, this);
23921             this.store.un('loadexception', this.onLoadException, this);
23922         }
23923         Roo.form.ComboBox.superclass.onDestroy.call(this);
23924     },
23925
23926     // private
23927     fireKey : function(e){
23928         if(e.isNavKeyPress() && !this.list.isVisible()){
23929             this.fireEvent("specialkey", this, e);
23930         }
23931     },
23932
23933     // private
23934     onResize: function(w, h){
23935         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
23936         
23937         if(typeof w != 'number'){
23938             // we do not handle it!?!?
23939             return;
23940         }
23941         var tw = this.trigger.getWidth();
23942         tw += this.addicon ? this.addicon.getWidth() : 0;
23943         tw += this.editicon ? this.editicon.getWidth() : 0;
23944         var x = w - tw;
23945         this.el.setWidth( this.adjustWidth('input', x));
23946             
23947         this.trigger.setStyle('left', x+'px');
23948         
23949         if(this.list && this.listWidth === undefined){
23950             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
23951             this.list.setWidth(lw);
23952             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23953         }
23954         
23955     
23956         
23957     },
23958
23959     /**
23960      * Allow or prevent the user from directly editing the field text.  If false is passed,
23961      * the user will only be able to select from the items defined in the dropdown list.  This method
23962      * is the runtime equivalent of setting the 'editable' config option at config time.
23963      * @param {Boolean} value True to allow the user to directly edit the field text
23964      */
23965     setEditable : function(value){
23966         if(value == this.editable){
23967             return;
23968         }
23969         this.editable = value;
23970         if(!value){
23971             this.el.dom.setAttribute('readOnly', true);
23972             this.el.on('mousedown', this.onTriggerClick,  this);
23973             this.el.addClass('x-combo-noedit');
23974         }else{
23975             this.el.dom.setAttribute('readOnly', false);
23976             this.el.un('mousedown', this.onTriggerClick,  this);
23977             this.el.removeClass('x-combo-noedit');
23978         }
23979     },
23980
23981     // private
23982     onBeforeLoad : function(){
23983         if(!this.hasFocus){
23984             return;
23985         }
23986         this.innerList.update(this.loadingText ?
23987                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
23988         this.restrictHeight();
23989         this.selectedIndex = -1;
23990     },
23991
23992     // private
23993     onLoad : function(){
23994         if(!this.hasFocus){
23995             return;
23996         }
23997         if(this.store.getCount() > 0){
23998             this.expand();
23999             this.restrictHeight();
24000             if(this.lastQuery == this.allQuery){
24001                 if(this.editable){
24002                     this.el.dom.select();
24003                 }
24004                 if(!this.selectByValue(this.value, true)){
24005                     this.select(0, true);
24006                 }
24007             }else{
24008                 this.selectNext();
24009                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
24010                     this.taTask.delay(this.typeAheadDelay);
24011                 }
24012             }
24013         }else{
24014             this.onEmptyResults();
24015         }
24016         //this.el.focus();
24017     },
24018     // private
24019     onLoadException : function()
24020     {
24021         this.collapse();
24022         Roo.log(this.store.reader.jsonData);
24023         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
24024             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
24025         }
24026         
24027         
24028     },
24029     // private
24030     onTypeAhead : function(){
24031         if(this.store.getCount() > 0){
24032             var r = this.store.getAt(0);
24033             var newValue = r.data[this.displayField];
24034             var len = newValue.length;
24035             var selStart = this.getRawValue().length;
24036             if(selStart != len){
24037                 this.setRawValue(newValue);
24038                 this.selectText(selStart, newValue.length);
24039             }
24040         }
24041     },
24042
24043     // private
24044     onSelect : function(record, index){
24045         if(this.fireEvent('beforeselect', this, record, index) !== false){
24046             this.setFromData(index > -1 ? record.data : false);
24047             this.collapse();
24048             this.fireEvent('select', this, record, index);
24049         }
24050     },
24051
24052     /**
24053      * Returns the currently selected field value or empty string if no value is set.
24054      * @return {String} value The selected value
24055      */
24056     getValue : function(){
24057         if(this.valueField){
24058             return typeof this.value != 'undefined' ? this.value : '';
24059         }else{
24060             return Roo.form.ComboBox.superclass.getValue.call(this);
24061         }
24062     },
24063
24064     /**
24065      * Clears any text/value currently set in the field
24066      */
24067     clearValue : function(){
24068         if(this.hiddenField){
24069             this.hiddenField.value = '';
24070         }
24071         this.value = '';
24072         this.setRawValue('');
24073         this.lastSelectionText = '';
24074         
24075     },
24076
24077     /**
24078      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
24079      * will be displayed in the field.  If the value does not match the data value of an existing item,
24080      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
24081      * Otherwise the field will be blank (although the value will still be set).
24082      * @param {String} value The value to match
24083      */
24084     setValue : function(v){
24085         var text = v;
24086         if(this.valueField){
24087             var r = this.findRecord(this.valueField, v);
24088             if(r){
24089                 text = r.data[this.displayField];
24090             }else if(this.valueNotFoundText !== undefined){
24091                 text = this.valueNotFoundText;
24092             }
24093         }
24094         this.lastSelectionText = text;
24095         if(this.hiddenField){
24096             this.hiddenField.value = v;
24097         }
24098         Roo.form.ComboBox.superclass.setValue.call(this, text);
24099         this.value = v;
24100     },
24101     /**
24102      * @property {Object} the last set data for the element
24103      */
24104     
24105     lastData : false,
24106     /**
24107      * Sets the value of the field based on a object which is related to the record format for the store.
24108      * @param {Object} value the value to set as. or false on reset?
24109      */
24110     setFromData : function(o){
24111         var dv = ''; // display value
24112         var vv = ''; // value value..
24113         this.lastData = o;
24114         if (this.displayField) {
24115             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
24116         } else {
24117             // this is an error condition!!!
24118             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
24119         }
24120         
24121         if(this.valueField){
24122             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
24123         }
24124         if(this.hiddenField){
24125             this.hiddenField.value = vv;
24126             
24127             this.lastSelectionText = dv;
24128             Roo.form.ComboBox.superclass.setValue.call(this, dv);
24129             this.value = vv;
24130             return;
24131         }
24132         // no hidden field.. - we store the value in 'value', but still display
24133         // display field!!!!
24134         this.lastSelectionText = dv;
24135         Roo.form.ComboBox.superclass.setValue.call(this, dv);
24136         this.value = vv;
24137         
24138         
24139     },
24140     // private
24141     reset : function(){
24142         // overridden so that last data is reset..
24143         this.setValue(this.originalValue);
24144         this.clearInvalid();
24145         this.lastData = false;
24146         if (this.view) {
24147             this.view.clearSelections();
24148         }
24149     },
24150     // private
24151     findRecord : function(prop, value){
24152         var record;
24153         if(this.store.getCount() > 0){
24154             this.store.each(function(r){
24155                 if(r.data[prop] == value){
24156                     record = r;
24157                     return false;
24158                 }
24159                 return true;
24160             });
24161         }
24162         return record;
24163     },
24164     
24165     getName: function()
24166     {
24167         // returns hidden if it's set..
24168         if (!this.rendered) {return ''};
24169         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
24170         
24171     },
24172     // private
24173     onViewMove : function(e, t){
24174         this.inKeyMode = false;
24175     },
24176
24177     // private
24178     onViewOver : function(e, t){
24179         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
24180             return;
24181         }
24182         var item = this.view.findItemFromChild(t);
24183         if(item){
24184             var index = this.view.indexOf(item);
24185             this.select(index, false);
24186         }
24187     },
24188
24189     // private
24190     onViewClick : function(doFocus)
24191     {
24192         var index = this.view.getSelectedIndexes()[0];
24193         var r = this.store.getAt(index);
24194         if(r){
24195             this.onSelect(r, index);
24196         }
24197         if(doFocus !== false && !this.blockFocus){
24198             this.el.focus();
24199         }
24200     },
24201
24202     // private
24203     restrictHeight : function(){
24204         this.innerList.dom.style.height = '';
24205         var inner = this.innerList.dom;
24206         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
24207         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
24208         this.list.beginUpdate();
24209         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
24210         this.list.alignTo(this.el, this.listAlign);
24211         this.list.endUpdate();
24212     },
24213
24214     // private
24215     onEmptyResults : function(){
24216         this.collapse();
24217     },
24218
24219     /**
24220      * Returns true if the dropdown list is expanded, else false.
24221      */
24222     isExpanded : function(){
24223         return this.list.isVisible();
24224     },
24225
24226     /**
24227      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
24228      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
24229      * @param {String} value The data value of the item to select
24230      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
24231      * selected item if it is not currently in view (defaults to true)
24232      * @return {Boolean} True if the value matched an item in the list, else false
24233      */
24234     selectByValue : function(v, scrollIntoView){
24235         if(v !== undefined && v !== null){
24236             var r = this.findRecord(this.valueField || this.displayField, v);
24237             if(r){
24238                 this.select(this.store.indexOf(r), scrollIntoView);
24239                 return true;
24240             }
24241         }
24242         return false;
24243     },
24244
24245     /**
24246      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
24247      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
24248      * @param {Number} index The zero-based index of the list item to select
24249      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
24250      * selected item if it is not currently in view (defaults to true)
24251      */
24252     select : function(index, scrollIntoView){
24253         this.selectedIndex = index;
24254         this.view.select(index);
24255         if(scrollIntoView !== false){
24256             var el = this.view.getNode(index);
24257             if(el){
24258                 this.innerList.scrollChildIntoView(el, false);
24259             }
24260         }
24261     },
24262
24263     // private
24264     selectNext : function(){
24265         var ct = this.store.getCount();
24266         if(ct > 0){
24267             if(this.selectedIndex == -1){
24268                 this.select(0);
24269             }else if(this.selectedIndex < ct-1){
24270                 this.select(this.selectedIndex+1);
24271             }
24272         }
24273     },
24274
24275     // private
24276     selectPrev : function(){
24277         var ct = this.store.getCount();
24278         if(ct > 0){
24279             if(this.selectedIndex == -1){
24280                 this.select(0);
24281             }else if(this.selectedIndex != 0){
24282                 this.select(this.selectedIndex-1);
24283             }
24284         }
24285     },
24286
24287     // private
24288     onKeyUp : function(e){
24289         if(this.editable !== false && !e.isSpecialKey()){
24290             this.lastKey = e.getKey();
24291             this.dqTask.delay(this.queryDelay);
24292         }
24293     },
24294
24295     // private
24296     validateBlur : function(){
24297         return !this.list || !this.list.isVisible();   
24298     },
24299
24300     // private
24301     initQuery : function(){
24302         this.doQuery(this.getRawValue());
24303     },
24304
24305     // private
24306     doForce : function(){
24307         if(this.el.dom.value.length > 0){
24308             this.el.dom.value =
24309                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
24310              
24311         }
24312     },
24313
24314     /**
24315      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
24316      * query allowing the query action to be canceled if needed.
24317      * @param {String} query The SQL query to execute
24318      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
24319      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
24320      * saved in the current store (defaults to false)
24321      */
24322     doQuery : function(q, forceAll){
24323         if(q === undefined || q === null){
24324             q = '';
24325         }
24326         var qe = {
24327             query: q,
24328             forceAll: forceAll,
24329             combo: this,
24330             cancel:false
24331         };
24332         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
24333             return false;
24334         }
24335         q = qe.query;
24336         forceAll = qe.forceAll;
24337         if(forceAll === true || (q.length >= this.minChars)){
24338             if(this.lastQuery != q || this.alwaysQuery){
24339                 this.lastQuery = q;
24340                 if(this.mode == 'local'){
24341                     this.selectedIndex = -1;
24342                     if(forceAll){
24343                         this.store.clearFilter();
24344                     }else{
24345                         this.store.filter(this.displayField, q);
24346                     }
24347                     this.onLoad();
24348                 }else{
24349                     this.store.baseParams[this.queryParam] = q;
24350                     this.store.load({
24351                         params: this.getParams(q)
24352                     });
24353                     this.expand();
24354                 }
24355             }else{
24356                 this.selectedIndex = -1;
24357                 this.onLoad();   
24358             }
24359         }
24360     },
24361
24362     // private
24363     getParams : function(q){
24364         var p = {};
24365         //p[this.queryParam] = q;
24366         if(this.pageSize){
24367             p.start = 0;
24368             p.limit = this.pageSize;
24369         }
24370         return p;
24371     },
24372
24373     /**
24374      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
24375      */
24376     collapse : function(){
24377         if(!this.isExpanded()){
24378             return;
24379         }
24380         this.list.hide();
24381         Roo.get(document).un('mousedown', this.collapseIf, this);
24382         Roo.get(document).un('mousewheel', this.collapseIf, this);
24383         if (!this.editable) {
24384             Roo.get(document).un('keydown', this.listKeyPress, this);
24385         }
24386         this.fireEvent('collapse', this);
24387     },
24388
24389     // private
24390     collapseIf : function(e){
24391         if(!e.within(this.wrap) && !e.within(this.list)){
24392             this.collapse();
24393         }
24394     },
24395
24396     /**
24397      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
24398      */
24399     expand : function(){
24400         if(this.isExpanded() || !this.hasFocus){
24401             return;
24402         }
24403         this.list.alignTo(this.el, this.listAlign);
24404         this.list.show();
24405         Roo.get(document).on('mousedown', this.collapseIf, this);
24406         Roo.get(document).on('mousewheel', this.collapseIf, this);
24407         if (!this.editable) {
24408             Roo.get(document).on('keydown', this.listKeyPress, this);
24409         }
24410         
24411         this.fireEvent('expand', this);
24412     },
24413
24414     // private
24415     // Implements the default empty TriggerField.onTriggerClick function
24416     onTriggerClick : function(){
24417         if(this.disabled){
24418             return;
24419         }
24420         if(this.isExpanded()){
24421             this.collapse();
24422             if (!this.blockFocus) {
24423                 this.el.focus();
24424             }
24425             
24426         }else {
24427             this.hasFocus = true;
24428             if(this.triggerAction == 'all') {
24429                 this.doQuery(this.allQuery, true);
24430             } else {
24431                 this.doQuery(this.getRawValue());
24432             }
24433             if (!this.blockFocus) {
24434                 this.el.focus();
24435             }
24436         }
24437     },
24438     listKeyPress : function(e)
24439     {
24440         //Roo.log('listkeypress');
24441         // scroll to first matching element based on key pres..
24442         if (e.isSpecialKey()) {
24443             return false;
24444         }
24445         var k = String.fromCharCode(e.getKey()).toUpperCase();
24446         //Roo.log(k);
24447         var match  = false;
24448         var csel = this.view.getSelectedNodes();
24449         var cselitem = false;
24450         if (csel.length) {
24451             var ix = this.view.indexOf(csel[0]);
24452             cselitem  = this.store.getAt(ix);
24453             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
24454                 cselitem = false;
24455             }
24456             
24457         }
24458         
24459         this.store.each(function(v) { 
24460             if (cselitem) {
24461                 // start at existing selection.
24462                 if (cselitem.id == v.id) {
24463                     cselitem = false;
24464                 }
24465                 return;
24466             }
24467                 
24468             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
24469                 match = this.store.indexOf(v);
24470                 return false;
24471             }
24472         }, this);
24473         
24474         if (match === false) {
24475             return true; // no more action?
24476         }
24477         // scroll to?
24478         this.view.select(match);
24479         var sn = Roo.get(this.view.getSelectedNodes()[0])
24480         sn.scrollIntoView(sn.dom.parentNode, false);
24481     }
24482
24483     /** 
24484     * @cfg {Boolean} grow 
24485     * @hide 
24486     */
24487     /** 
24488     * @cfg {Number} growMin 
24489     * @hide 
24490     */
24491     /** 
24492     * @cfg {Number} growMax 
24493     * @hide 
24494     */
24495     /**
24496      * @hide
24497      * @method autoSize
24498      */
24499 });/*
24500  * Copyright(c) 2010-2012, Roo J Solutions Limited
24501  *
24502  * Licence LGPL
24503  *
24504  */
24505
24506 /**
24507  * @class Roo.form.ComboBoxArray
24508  * @extends Roo.form.TextField
24509  * A facebook style adder... for lists of email / people / countries  etc...
24510  * pick multiple items from a combo box, and shows each one.
24511  *
24512  *  Fred [x]  Brian [x]  [Pick another |v]
24513  *
24514  *
24515  *  For this to work: it needs various extra information
24516  *    - normal combo problay has
24517  *      name, hiddenName
24518  *    + displayField, valueField
24519  *
24520  *    For our purpose...
24521  *
24522  *
24523  *   If we change from 'extends' to wrapping...
24524  *   
24525  *  
24526  *
24527  
24528  
24529  * @constructor
24530  * Create a new ComboBoxArray.
24531  * @param {Object} config Configuration options
24532  */
24533  
24534
24535 Roo.form.ComboBoxArray = function(config)
24536 {
24537     
24538     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
24539     
24540     this.items = new Roo.util.MixedCollection(false);
24541     
24542     // construct the child combo...
24543     
24544     
24545     
24546     
24547    
24548     
24549 }
24550
24551  
24552 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
24553
24554     /**
24555      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
24556      */
24557     
24558     lastData : false,
24559     
24560     // behavies liek a hiddne field
24561     inputType:      'hidden',
24562     /**
24563      * @cfg {Number} width The width of the box that displays the selected element
24564      */ 
24565     width:          300,
24566
24567     
24568     
24569     /**
24570      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
24571      */
24572     name : false,
24573     /**
24574      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
24575      */
24576     hiddenName : false,
24577     
24578     
24579     // private the array of items that are displayed..
24580     items  : false,
24581     // private - the hidden field el.
24582     hiddenEl : false,
24583     // private - the filed el..
24584     el : false,
24585     
24586     //validateValue : function() { return true; }, // all values are ok!
24587     //onAddClick: function() { },
24588     
24589     onRender : function(ct, position) 
24590     {
24591         
24592         // create the standard hidden element
24593         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
24594         
24595         
24596         // give fake names to child combo;
24597         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
24598         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
24599         
24600         this.combo = Roo.factory(this.combo, Roo.form);
24601         this.combo.onRender(ct, position);
24602         if (typeof(this.combo.width) != 'undefined') {
24603             this.combo.onResize(this.combo.width,0);
24604         }
24605         
24606         this.combo.initEvents();
24607         
24608         // assigned so form know we need to do this..
24609         this.store          = this.combo.store;
24610         this.valueField     = this.combo.valueField;
24611         this.displayField   = this.combo.displayField ;
24612         
24613         
24614         this.combo.wrap.addClass('x-cbarray-grp');
24615         
24616         var cbwrap = this.combo.wrap.createChild(
24617             {tag: 'div', cls: 'x-cbarray-cb'},
24618             this.combo.el.dom
24619         );
24620         
24621              
24622         this.hiddenEl = this.combo.wrap.createChild({
24623             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
24624         });
24625         this.el = this.combo.wrap.createChild({
24626             tag: 'input',  type:'hidden' , name: this.name, value : ''
24627         });
24628          //   this.el.dom.removeAttribute("name");
24629         
24630         
24631         this.outerWrap = this.combo.wrap;
24632         this.wrap = cbwrap;
24633         
24634         this.outerWrap.setWidth(this.width);
24635         this.outerWrap.dom.removeChild(this.el.dom);
24636         
24637         this.wrap.dom.appendChild(this.el.dom);
24638         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
24639         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
24640         
24641         this.combo.trigger.setStyle('position','relative');
24642         this.combo.trigger.setStyle('left', '0px');
24643         this.combo.trigger.setStyle('top', '2px');
24644         
24645         this.combo.el.setStyle('vertical-align', 'text-bottom');
24646         
24647         //this.trigger.setStyle('vertical-align', 'top');
24648         
24649         // this should use the code from combo really... on('add' ....)
24650         if (this.adder) {
24651             
24652         
24653             this.adder = this.outerWrap.createChild(
24654                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
24655             var _t = this;
24656             this.adder.on('click', function(e) {
24657                 _t.fireEvent('adderclick', this, e);
24658             }, _t);
24659         }
24660         //var _t = this;
24661         //this.adder.on('click', this.onAddClick, _t);
24662         
24663         
24664         this.combo.on('select', function(cb, rec, ix) {
24665             this.addItem(rec.data);
24666             
24667             cb.setValue('');
24668             cb.el.dom.value = '';
24669             //cb.lastData = rec.data;
24670             // add to list
24671             
24672         }, this);
24673         
24674         
24675     },
24676     
24677     
24678     getName: function()
24679     {
24680         // returns hidden if it's set..
24681         if (!this.rendered) {return ''};
24682         return  this.hiddenName ? this.hiddenName : this.name;
24683         
24684     },
24685     
24686     
24687     onResize: function(w, h){
24688         
24689         return;
24690         // not sure if this is needed..
24691         //this.combo.onResize(w,h);
24692         
24693         if(typeof w != 'number'){
24694             // we do not handle it!?!?
24695             return;
24696         }
24697         var tw = this.combo.trigger.getWidth();
24698         tw += this.addicon ? this.addicon.getWidth() : 0;
24699         tw += this.editicon ? this.editicon.getWidth() : 0;
24700         var x = w - tw;
24701         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
24702             
24703         this.combo.trigger.setStyle('left', '0px');
24704         
24705         if(this.list && this.listWidth === undefined){
24706             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
24707             this.list.setWidth(lw);
24708             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
24709         }
24710         
24711     
24712         
24713     },
24714     
24715     addItem: function(rec)
24716     {
24717         var valueField = this.combo.valueField;
24718         var displayField = this.combo.displayField;
24719         if (this.items.indexOfKey(rec[valueField]) > -1) {
24720             //console.log("GOT " + rec.data.id);
24721             return;
24722         }
24723         
24724         var x = new Roo.form.ComboBoxArray.Item({
24725             //id : rec[this.idField],
24726             data : rec,
24727             displayField : displayField ,
24728             tipField : displayField ,
24729             cb : this
24730         });
24731         // use the 
24732         this.items.add(rec[valueField],x);
24733         // add it before the element..
24734         this.updateHiddenEl();
24735         x.render(this.outerWrap, this.wrap.dom);
24736         // add the image handler..
24737     },
24738     
24739     updateHiddenEl : function()
24740     {
24741         this.validate();
24742         if (!this.hiddenEl) {
24743             return;
24744         }
24745         var ar = [];
24746         var idField = this.combo.valueField;
24747         
24748         this.items.each(function(f) {
24749             ar.push(f.data[idField]);
24750            
24751         });
24752         this.hiddenEl.dom.value = ar.join(',');
24753         this.validate();
24754     },
24755     
24756     reset : function()
24757     {
24758         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
24759         this.items.each(function(f) {
24760            f.remove(); 
24761         });
24762         this.el.dom.value = '';
24763         if (this.hiddenEl) {
24764             this.hiddenEl.dom.value = '';
24765         }
24766         
24767     },
24768     getValue: function()
24769     {
24770         return this.hiddenEl ? this.hiddenEl.dom.value : '';
24771     },
24772     setValue: function(v) // not a valid action - must use addItems..
24773     {
24774          
24775         this.reset();
24776         
24777         
24778         
24779         if (this.store.isLocal && (typeof(v) == 'string')) {
24780             // then we can use the store to find the values..
24781             // comma seperated at present.. this needs to allow JSON based encoding..
24782             this.hiddenEl.value  = v;
24783             var v_ar = [];
24784             Roo.each(v.split(','), function(k) {
24785                 Roo.log("CHECK " + this.valueField + ',' + k);
24786                 var li = this.store.query(this.valueField, k);
24787                 if (!li.length) {
24788                     return;
24789                 }
24790                 var add = {};
24791                 add[this.valueField] = k;
24792                 add[this.displayField] = li.item(0).data[this.displayField];
24793                 
24794                 this.addItem(add);
24795             }, this) 
24796              
24797         }
24798         if (typeof(v) == 'object') {
24799             // then let's assume it's an array of objects..
24800             Roo.each(v, function(l) {
24801                 this.addItem(l);
24802             }, this);
24803              
24804         }
24805         
24806         
24807     },
24808     setFromData: function(v)
24809     {
24810         // this recieves an object, if setValues is called.
24811         this.reset();
24812         this.el.dom.value = v[this.displayField];
24813         this.hiddenEl.dom.value = v[this.valueField];
24814         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
24815             return;
24816         }
24817         var kv = v[this.valueField];
24818         var dv = v[this.displayField];
24819         kv = typeof(kv) != 'string' ? '' : kv;
24820         dv = typeof(dv) != 'string' ? '' : dv;
24821         
24822         
24823         var keys = kv.split(',');
24824         var display = dv.split(',');
24825         for (var i = 0 ; i < keys.length; i++) {
24826             
24827             add = {};
24828             add[this.valueField] = keys[i];
24829             add[this.displayField] = display[i];
24830             this.addItem(add);
24831         }
24832       
24833         
24834     },
24835     
24836     
24837     validateValue : function(value){
24838         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
24839         
24840     }
24841     
24842 });
24843
24844
24845
24846 /**
24847  * @class Roo.form.ComboBoxArray.Item
24848  * @extends Roo.BoxComponent
24849  * A selected item in the list
24850  *  Fred [x]  Brian [x]  [Pick another |v]
24851  * 
24852  * @constructor
24853  * Create a new item.
24854  * @param {Object} config Configuration options
24855  */
24856  
24857 Roo.form.ComboBoxArray.Item = function(config) {
24858     config.id = Roo.id();
24859     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
24860 }
24861
24862 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
24863     data : {},
24864     cb: false,
24865     displayField : false,
24866     tipField : false,
24867     
24868     
24869     defaultAutoCreate : {
24870         tag: 'div',
24871         cls: 'x-cbarray-item',
24872         cn : [ 
24873             { tag: 'div' },
24874             {
24875                 tag: 'img',
24876                 width:16,
24877                 height : 16,
24878                 src : Roo.BLANK_IMAGE_URL ,
24879                 align: 'center'
24880             }
24881         ]
24882         
24883     },
24884     
24885  
24886     onRender : function(ct, position)
24887     {
24888         Roo.form.Field.superclass.onRender.call(this, ct, position);
24889         
24890         if(!this.el){
24891             var cfg = this.getAutoCreate();
24892             this.el = ct.createChild(cfg, position);
24893         }
24894         
24895         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
24896         
24897         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
24898             this.cb.renderer(this.data) :
24899             String.format('{0}',this.data[this.displayField]);
24900         
24901             
24902         this.el.child('div').dom.setAttribute('qtip',
24903                         String.format('{0}',this.data[this.tipField])
24904         );
24905         
24906         this.el.child('img').on('click', this.remove, this);
24907         
24908     },
24909    
24910     remove : function()
24911     {
24912         
24913         this.cb.items.remove(this);
24914         this.el.child('img').un('click', this.remove, this);
24915         this.el.remove();
24916         this.cb.updateHiddenEl();
24917     }
24918     
24919     
24920 });/*
24921  * Based on:
24922  * Ext JS Library 1.1.1
24923  * Copyright(c) 2006-2007, Ext JS, LLC.
24924  *
24925  * Originally Released Under LGPL - original licence link has changed is not relivant.
24926  *
24927  * Fork - LGPL
24928  * <script type="text/javascript">
24929  */
24930 /**
24931  * @class Roo.form.Checkbox
24932  * @extends Roo.form.Field
24933  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
24934  * @constructor
24935  * Creates a new Checkbox
24936  * @param {Object} config Configuration options
24937  */
24938 Roo.form.Checkbox = function(config){
24939     Roo.form.Checkbox.superclass.constructor.call(this, config);
24940     this.addEvents({
24941         /**
24942          * @event check
24943          * Fires when the checkbox is checked or unchecked.
24944              * @param {Roo.form.Checkbox} this This checkbox
24945              * @param {Boolean} checked The new checked value
24946              */
24947         check : true
24948     });
24949 };
24950
24951 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
24952     /**
24953      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
24954      */
24955     focusClass : undefined,
24956     /**
24957      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
24958      */
24959     fieldClass: "x-form-field",
24960     /**
24961      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
24962      */
24963     checked: false,
24964     /**
24965      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
24966      * {tag: "input", type: "checkbox", autocomplete: "off"})
24967      */
24968     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
24969     /**
24970      * @cfg {String} boxLabel The text that appears beside the checkbox
24971      */
24972     boxLabel : "",
24973     /**
24974      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
24975      */  
24976     inputValue : '1',
24977     /**
24978      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24979      */
24980      valueOff: '0', // value when not checked..
24981
24982     actionMode : 'viewEl', 
24983     //
24984     // private
24985     itemCls : 'x-menu-check-item x-form-item',
24986     groupClass : 'x-menu-group-item',
24987     inputType : 'hidden',
24988     
24989     
24990     inSetChecked: false, // check that we are not calling self...
24991     
24992     inputElement: false, // real input element?
24993     basedOn: false, // ????
24994     
24995     isFormField: true, // not sure where this is needed!!!!
24996
24997     onResize : function(){
24998         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
24999         if(!this.boxLabel){
25000             this.el.alignTo(this.wrap, 'c-c');
25001         }
25002     },
25003
25004     initEvents : function(){
25005         Roo.form.Checkbox.superclass.initEvents.call(this);
25006         this.el.on("click", this.onClick,  this);
25007         this.el.on("change", this.onClick,  this);
25008     },
25009
25010
25011     getResizeEl : function(){
25012         return this.wrap;
25013     },
25014
25015     getPositionEl : function(){
25016         return this.wrap;
25017     },
25018
25019     // private
25020     onRender : function(ct, position){
25021         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
25022         /*
25023         if(this.inputValue !== undefined){
25024             this.el.dom.value = this.inputValue;
25025         }
25026         */
25027         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
25028         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
25029         var viewEl = this.wrap.createChild({ 
25030             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
25031         this.viewEl = viewEl;   
25032         this.wrap.on('click', this.onClick,  this); 
25033         
25034         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
25035         this.el.on('propertychange', this.setFromHidden,  this);  //ie
25036         
25037         
25038         
25039         if(this.boxLabel){
25040             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
25041         //    viewEl.on('click', this.onClick,  this); 
25042         }
25043         //if(this.checked){
25044             this.setChecked(this.checked);
25045         //}else{
25046             //this.checked = this.el.dom;
25047         //}
25048
25049     },
25050
25051     // private
25052     initValue : Roo.emptyFn,
25053
25054     /**
25055      * Returns the checked state of the checkbox.
25056      * @return {Boolean} True if checked, else false
25057      */
25058     getValue : function(){
25059         if(this.el){
25060             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
25061         }
25062         return this.valueOff;
25063         
25064     },
25065
25066         // private
25067     onClick : function(){ 
25068         this.setChecked(!this.checked);
25069
25070         //if(this.el.dom.checked != this.checked){
25071         //    this.setValue(this.el.dom.checked);
25072        // }
25073     },
25074
25075     /**
25076      * Sets the checked state of the checkbox.
25077      * On is always based on a string comparison between inputValue and the param.
25078      * @param {Boolean/String} value - the value to set 
25079      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
25080      */
25081     setValue : function(v,suppressEvent){
25082         
25083         
25084         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
25085         //if(this.el && this.el.dom){
25086         //    this.el.dom.checked = this.checked;
25087         //    this.el.dom.defaultChecked = this.checked;
25088         //}
25089         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
25090         //this.fireEvent("check", this, this.checked);
25091     },
25092     // private..
25093     setChecked : function(state,suppressEvent)
25094     {
25095         if (this.inSetChecked) {
25096             this.checked = state;
25097             return;
25098         }
25099         
25100     
25101         if(this.wrap){
25102             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
25103         }
25104         this.checked = state;
25105         if(suppressEvent !== true){
25106             this.fireEvent('check', this, state);
25107         }
25108         this.inSetChecked = true;
25109         this.el.dom.value = state ? this.inputValue : this.valueOff;
25110         this.inSetChecked = false;
25111         
25112     },
25113     // handle setting of hidden value by some other method!!?!?
25114     setFromHidden: function()
25115     {
25116         if(!this.el){
25117             return;
25118         }
25119         //console.log("SET FROM HIDDEN");
25120         //alert('setFrom hidden');
25121         this.setValue(this.el.dom.value);
25122     },
25123     
25124     onDestroy : function()
25125     {
25126         if(this.viewEl){
25127             Roo.get(this.viewEl).remove();
25128         }
25129          
25130         Roo.form.Checkbox.superclass.onDestroy.call(this);
25131     }
25132
25133 });/*
25134  * Based on:
25135  * Ext JS Library 1.1.1
25136  * Copyright(c) 2006-2007, Ext JS, LLC.
25137  *
25138  * Originally Released Under LGPL - original licence link has changed is not relivant.
25139  *
25140  * Fork - LGPL
25141  * <script type="text/javascript">
25142  */
25143  
25144 /**
25145  * @class Roo.form.Radio
25146  * @extends Roo.form.Checkbox
25147  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
25148  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
25149  * @constructor
25150  * Creates a new Radio
25151  * @param {Object} config Configuration options
25152  */
25153 Roo.form.Radio = function(){
25154     Roo.form.Radio.superclass.constructor.apply(this, arguments);
25155 };
25156 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
25157     inputType: 'radio',
25158
25159     /**
25160      * If this radio is part of a group, it will return the selected value
25161      * @return {String}
25162      */
25163     getGroupValue : function(){
25164         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
25165     },
25166     
25167     
25168     onRender : function(ct, position){
25169         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
25170         
25171         if(this.inputValue !== undefined){
25172             this.el.dom.value = this.inputValue;
25173         }
25174          
25175         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
25176         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
25177         //var viewEl = this.wrap.createChild({ 
25178         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
25179         //this.viewEl = viewEl;   
25180         //this.wrap.on('click', this.onClick,  this); 
25181         
25182         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
25183         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
25184         
25185         
25186         
25187         if(this.boxLabel){
25188             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
25189         //    viewEl.on('click', this.onClick,  this); 
25190         }
25191          if(this.checked){
25192             this.el.dom.checked =   'checked' ;
25193         }
25194          
25195     } 
25196     
25197     
25198 });//<script type="text/javascript">
25199
25200 /*
25201  * Ext JS Library 1.1.1
25202  * Copyright(c) 2006-2007, Ext JS, LLC.
25203  * licensing@extjs.com
25204  * 
25205  * http://www.extjs.com/license
25206  */
25207  
25208  /*
25209   * 
25210   * Known bugs:
25211   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
25212   * - IE ? - no idea how much works there.
25213   * 
25214   * 
25215   * 
25216   */
25217  
25218
25219 /**
25220  * @class Ext.form.HtmlEditor
25221  * @extends Ext.form.Field
25222  * Provides a lightweight HTML Editor component.
25223  *
25224  * This has been tested on Fireforx / Chrome.. IE may not be so great..
25225  * 
25226  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
25227  * supported by this editor.</b><br/><br/>
25228  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
25229  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25230  */
25231 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
25232       /**
25233      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25234      */
25235     toolbars : false,
25236     /**
25237      * @cfg {String} createLinkText The default text for the create link prompt
25238      */
25239     createLinkText : 'Please enter the URL for the link:',
25240     /**
25241      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
25242      */
25243     defaultLinkValue : 'http:/'+'/',
25244    
25245      /**
25246      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25247      *                        Roo.resizable.
25248      */
25249     resizable : false,
25250      /**
25251      * @cfg {Number} height (in pixels)
25252      */   
25253     height: 300,
25254    /**
25255      * @cfg {Number} width (in pixels)
25256      */   
25257     width: 500,
25258     
25259     /**
25260      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25261      * 
25262      */
25263     stylesheets: false,
25264     
25265     // id of frame..
25266     frameId: false,
25267     
25268     // private properties
25269     validationEvent : false,
25270     deferHeight: true,
25271     initialized : false,
25272     activated : false,
25273     sourceEditMode : false,
25274     onFocus : Roo.emptyFn,
25275     iframePad:3,
25276     hideMode:'offsets',
25277     
25278     defaultAutoCreate : { // modified by initCompnoent..
25279         tag: "textarea",
25280         style:"width:500px;height:300px;",
25281         autocomplete: "off"
25282     },
25283
25284     // private
25285     initComponent : function(){
25286         this.addEvents({
25287             /**
25288              * @event initialize
25289              * Fires when the editor is fully initialized (including the iframe)
25290              * @param {HtmlEditor} this
25291              */
25292             initialize: true,
25293             /**
25294              * @event activate
25295              * Fires when the editor is first receives the focus. Any insertion must wait
25296              * until after this event.
25297              * @param {HtmlEditor} this
25298              */
25299             activate: true,
25300              /**
25301              * @event beforesync
25302              * Fires before the textarea is updated with content from the editor iframe. Return false
25303              * to cancel the sync.
25304              * @param {HtmlEditor} this
25305              * @param {String} html
25306              */
25307             beforesync: true,
25308              /**
25309              * @event beforepush
25310              * Fires before the iframe editor is updated with content from the textarea. Return false
25311              * to cancel the push.
25312              * @param {HtmlEditor} this
25313              * @param {String} html
25314              */
25315             beforepush: true,
25316              /**
25317              * @event sync
25318              * Fires when the textarea is updated with content from the editor iframe.
25319              * @param {HtmlEditor} this
25320              * @param {String} html
25321              */
25322             sync: true,
25323              /**
25324              * @event push
25325              * Fires when the iframe editor is updated with content from the textarea.
25326              * @param {HtmlEditor} this
25327              * @param {String} html
25328              */
25329             push: true,
25330              /**
25331              * @event editmodechange
25332              * Fires when the editor switches edit modes
25333              * @param {HtmlEditor} this
25334              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25335              */
25336             editmodechange: true,
25337             /**
25338              * @event editorevent
25339              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25340              * @param {HtmlEditor} this
25341              */
25342             editorevent: true
25343         });
25344         this.defaultAutoCreate =  {
25345             tag: "textarea",
25346             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
25347             autocomplete: "off"
25348         };
25349     },
25350
25351     /**
25352      * Protected method that will not generally be called directly. It
25353      * is called when the editor creates its toolbar. Override this method if you need to
25354      * add custom toolbar buttons.
25355      * @param {HtmlEditor} editor
25356      */
25357     createToolbar : function(editor){
25358         if (!editor.toolbars || !editor.toolbars.length) {
25359             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
25360         }
25361         
25362         for (var i =0 ; i < editor.toolbars.length;i++) {
25363             editor.toolbars[i] = Roo.factory(
25364                     typeof(editor.toolbars[i]) == 'string' ?
25365                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
25366                 Roo.form.HtmlEditor);
25367             editor.toolbars[i].init(editor);
25368         }
25369          
25370         
25371     },
25372
25373     /**
25374      * Protected method that will not generally be called directly. It
25375      * is called when the editor initializes the iframe with HTML contents. Override this method if you
25376      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25377      */
25378     getDocMarkup : function(){
25379         // body styles..
25380         var st = '';
25381         if (this.stylesheets === false) {
25382             
25383             Roo.get(document.head).select('style').each(function(node) {
25384                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25385             });
25386             
25387             Roo.get(document.head).select('link').each(function(node) { 
25388                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25389             });
25390             
25391         } else if (!this.stylesheets.length) {
25392                 // simple..
25393                 st = '<style type="text/css">' +
25394                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25395                    '</style>';
25396         } else {
25397             Roo.each(this.stylesheets, function(s) {
25398                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
25399             });
25400             
25401         }
25402         
25403         st +=  '<style type="text/css">' +
25404             'IMG { cursor: pointer } ' +
25405         '</style>';
25406
25407         
25408         return '<html><head>' + st  +
25409             //<style type="text/css">' +
25410             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25411             //'</style>' +
25412             ' </head><body class="roo-htmleditor-body"></body></html>';
25413     },
25414
25415     // private
25416     onRender : function(ct, position)
25417     {
25418         var _t = this;
25419         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
25420         this.el.dom.style.border = '0 none';
25421         this.el.dom.setAttribute('tabIndex', -1);
25422         this.el.addClass('x-hidden');
25423         if(Roo.isIE){ // fix IE 1px bogus margin
25424             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25425         }
25426         this.wrap = this.el.wrap({
25427             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25428         });
25429         
25430         if (this.resizable) {
25431             this.resizeEl = new Roo.Resizable(this.wrap, {
25432                 pinned : true,
25433                 wrap: true,
25434                 dynamic : true,
25435                 minHeight : this.height,
25436                 height: this.height,
25437                 handles : this.resizable,
25438                 width: this.width,
25439                 listeners : {
25440                     resize : function(r, w, h) {
25441                         _t.onResize(w,h); // -something
25442                     }
25443                 }
25444             });
25445             
25446         }
25447
25448         this.frameId = Roo.id();
25449         
25450         this.createToolbar(this);
25451         
25452       
25453         
25454         var iframe = this.wrap.createChild({
25455             tag: 'iframe',
25456             id: this.frameId,
25457             name: this.frameId,
25458             frameBorder : 'no',
25459             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
25460         }, this.el
25461         );
25462         
25463        // console.log(iframe);
25464         //this.wrap.dom.appendChild(iframe);
25465
25466         this.iframe = iframe.dom;
25467
25468          this.assignDocWin();
25469         
25470         this.doc.designMode = 'on';
25471        
25472         this.doc.open();
25473         this.doc.write(this.getDocMarkup());
25474         this.doc.close();
25475
25476         
25477         var task = { // must defer to wait for browser to be ready
25478             run : function(){
25479                 //console.log("run task?" + this.doc.readyState);
25480                 this.assignDocWin();
25481                 if(this.doc.body || this.doc.readyState == 'complete'){
25482                     try {
25483                         this.doc.designMode="on";
25484                     } catch (e) {
25485                         return;
25486                     }
25487                     Roo.TaskMgr.stop(task);
25488                     this.initEditor.defer(10, this);
25489                 }
25490             },
25491             interval : 10,
25492             duration:10000,
25493             scope: this
25494         };
25495         Roo.TaskMgr.start(task);
25496
25497         if(!this.width){
25498             this.setSize(this.wrap.getSize());
25499         }
25500         if (this.resizeEl) {
25501             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25502             // should trigger onReize..
25503         }
25504     },
25505
25506     // private
25507     onResize : function(w, h)
25508     {
25509         //Roo.log('resize: ' +w + ',' + h );
25510         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
25511         if(this.el && this.iframe){
25512             if(typeof w == 'number'){
25513                 var aw = w - this.wrap.getFrameWidth('lr');
25514                 this.el.setWidth(this.adjustWidth('textarea', aw));
25515                 this.iframe.style.width = aw + 'px';
25516             }
25517             if(typeof h == 'number'){
25518                 var tbh = 0;
25519                 for (var i =0; i < this.toolbars.length;i++) {
25520                     // fixme - ask toolbars for heights?
25521                     tbh += this.toolbars[i].tb.el.getHeight();
25522                     if (this.toolbars[i].footer) {
25523                         tbh += this.toolbars[i].footer.el.getHeight();
25524                     }
25525                 }
25526                 
25527                 
25528                 
25529                 
25530                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25531                 ah -= 5; // knock a few pixes off for look..
25532                 this.el.setHeight(this.adjustWidth('textarea', ah));
25533                 this.iframe.style.height = ah + 'px';
25534                 if(this.doc){
25535                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
25536                 }
25537             }
25538         }
25539     },
25540
25541     /**
25542      * Toggles the editor between standard and source edit mode.
25543      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25544      */
25545     toggleSourceEdit : function(sourceEditMode){
25546         
25547         this.sourceEditMode = sourceEditMode === true;
25548         
25549         if(this.sourceEditMode){
25550 //            Roo.log('in');
25551 //            Roo.log(this.syncValue());
25552             this.syncValue();
25553             this.iframe.className = 'x-hidden';
25554             this.el.removeClass('x-hidden');
25555             this.el.dom.removeAttribute('tabIndex');
25556             this.el.focus();
25557         }else{
25558 //            Roo.log('out')
25559 //            Roo.log(this.pushValue()); 
25560             this.pushValue();
25561             this.iframe.className = '';
25562             this.el.addClass('x-hidden');
25563             this.el.dom.setAttribute('tabIndex', -1);
25564             this.deferFocus();
25565         }
25566         this.setSize(this.wrap.getSize());
25567         this.fireEvent('editmodechange', this, this.sourceEditMode);
25568     },
25569
25570     // private used internally
25571     createLink : function(){
25572         var url = prompt(this.createLinkText, this.defaultLinkValue);
25573         if(url && url != 'http:/'+'/'){
25574             this.relayCmd('createlink', url);
25575         }
25576     },
25577
25578     // private (for BoxComponent)
25579     adjustSize : Roo.BoxComponent.prototype.adjustSize,
25580
25581     // private (for BoxComponent)
25582     getResizeEl : function(){
25583         return this.wrap;
25584     },
25585
25586     // private (for BoxComponent)
25587     getPositionEl : function(){
25588         return this.wrap;
25589     },
25590
25591     // private
25592     initEvents : function(){
25593         this.originalValue = this.getValue();
25594     },
25595
25596     /**
25597      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25598      * @method
25599      */
25600     markInvalid : Roo.emptyFn,
25601     /**
25602      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25603      * @method
25604      */
25605     clearInvalid : Roo.emptyFn,
25606
25607     setValue : function(v){
25608         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
25609         this.pushValue();
25610     },
25611
25612     /**
25613      * Protected method that will not generally be called directly. If you need/want
25614      * custom HTML cleanup, this is the method you should override.
25615      * @param {String} html The HTML to be cleaned
25616      * return {String} The cleaned HTML
25617      */
25618     cleanHtml : function(html){
25619         html = String(html);
25620         if(html.length > 5){
25621             if(Roo.isSafari){ // strip safari nonsense
25622                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25623             }
25624         }
25625         if(html == '&nbsp;'){
25626             html = '';
25627         }
25628         return html;
25629     },
25630
25631     /**
25632      * Protected method that will not generally be called directly. Syncs the contents
25633      * of the editor iframe with the textarea.
25634      */
25635     syncValue : function(){
25636         if(this.initialized){
25637             var bd = (this.doc.body || this.doc.documentElement);
25638             //this.cleanUpPaste(); -- this is done else where and causes havoc..
25639             var html = bd.innerHTML;
25640             if(Roo.isSafari){
25641                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25642                 var m = bs.match(/text-align:(.*?);/i);
25643                 if(m && m[1]){
25644                     html = '<div style="'+m[0]+'">' + html + '</div>';
25645                 }
25646             }
25647             html = this.cleanHtml(html);
25648             // fix up the special chars.. normaly like back quotes in word...
25649             // however we do not want to do this with chinese..
25650             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
25651                 var cc = b.charCodeAt();
25652                 if (
25653                     (cc >= 0x4E00 && cc < 0xA000 ) ||
25654                     (cc >= 0x3400 && cc < 0x4E00 ) ||
25655                     (cc >= 0xf900 && cc < 0xfb00 )
25656                 ) {
25657                         return b;
25658                 }
25659                 return "&#"+cc+";" 
25660             });
25661             if(this.fireEvent('beforesync', this, html) !== false){
25662                 this.el.dom.value = html;
25663                 this.fireEvent('sync', this, html);
25664             }
25665         }
25666     },
25667
25668     /**
25669      * Protected method that will not generally be called directly. Pushes the value of the textarea
25670      * into the iframe editor.
25671      */
25672     pushValue : function(){
25673         if(this.initialized){
25674             var v = this.el.dom.value;
25675             
25676             if(v.length < 1){
25677                 v = '&#160;';
25678             }
25679             
25680             if(this.fireEvent('beforepush', this, v) !== false){
25681                 var d = (this.doc.body || this.doc.documentElement);
25682                 d.innerHTML = v;
25683                 this.cleanUpPaste();
25684                 this.el.dom.value = d.innerHTML;
25685                 this.fireEvent('push', this, v);
25686             }
25687         }
25688     },
25689
25690     // private
25691     deferFocus : function(){
25692         this.focus.defer(10, this);
25693     },
25694
25695     // doc'ed in Field
25696     focus : function(){
25697         if(this.win && !this.sourceEditMode){
25698             this.win.focus();
25699         }else{
25700             this.el.focus();
25701         }
25702     },
25703     
25704     assignDocWin: function()
25705     {
25706         var iframe = this.iframe;
25707         
25708          if(Roo.isIE){
25709             this.doc = iframe.contentWindow.document;
25710             this.win = iframe.contentWindow;
25711         } else {
25712             if (!Roo.get(this.frameId)) {
25713                 return;
25714             }
25715             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25716             this.win = Roo.get(this.frameId).dom.contentWindow;
25717         }
25718     },
25719     
25720     // private
25721     initEditor : function(){
25722         //console.log("INIT EDITOR");
25723         this.assignDocWin();
25724         
25725         
25726         
25727         this.doc.designMode="on";
25728         this.doc.open();
25729         this.doc.write(this.getDocMarkup());
25730         this.doc.close();
25731         
25732         var dbody = (this.doc.body || this.doc.documentElement);
25733         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25734         // this copies styles from the containing element into thsi one..
25735         // not sure why we need all of this..
25736         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25737         ss['background-attachment'] = 'fixed'; // w3c
25738         dbody.bgProperties = 'fixed'; // ie
25739         Roo.DomHelper.applyStyles(dbody, ss);
25740         Roo.EventManager.on(this.doc, {
25741             //'mousedown': this.onEditorEvent,
25742             'mouseup': this.onEditorEvent,
25743             'dblclick': this.onEditorEvent,
25744             'click': this.onEditorEvent,
25745             'keyup': this.onEditorEvent,
25746             buffer:100,
25747             scope: this
25748         });
25749         if(Roo.isGecko){
25750             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25751         }
25752         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25753             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25754         }
25755         this.initialized = true;
25756
25757         this.fireEvent('initialize', this);
25758         this.pushValue();
25759     },
25760
25761     // private
25762     onDestroy : function(){
25763         
25764         
25765         
25766         if(this.rendered){
25767             
25768             for (var i =0; i < this.toolbars.length;i++) {
25769                 // fixme - ask toolbars for heights?
25770                 this.toolbars[i].onDestroy();
25771             }
25772             
25773             this.wrap.dom.innerHTML = '';
25774             this.wrap.remove();
25775         }
25776     },
25777
25778     // private
25779     onFirstFocus : function(){
25780         
25781         this.assignDocWin();
25782         
25783         
25784         this.activated = true;
25785         for (var i =0; i < this.toolbars.length;i++) {
25786             this.toolbars[i].onFirstFocus();
25787         }
25788        
25789         if(Roo.isGecko){ // prevent silly gecko errors
25790             this.win.focus();
25791             var s = this.win.getSelection();
25792             if(!s.focusNode || s.focusNode.nodeType != 3){
25793                 var r = s.getRangeAt(0);
25794                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25795                 r.collapse(true);
25796                 this.deferFocus();
25797             }
25798             try{
25799                 this.execCmd('useCSS', true);
25800                 this.execCmd('styleWithCSS', false);
25801             }catch(e){}
25802         }
25803         this.fireEvent('activate', this);
25804     },
25805
25806     // private
25807     adjustFont: function(btn){
25808         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
25809         //if(Roo.isSafari){ // safari
25810         //    adjust *= 2;
25811        // }
25812         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
25813         if(Roo.isSafari){ // safari
25814             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
25815             v =  (v < 10) ? 10 : v;
25816             v =  (v > 48) ? 48 : v;
25817             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
25818             
25819         }
25820         
25821         
25822         v = Math.max(1, v+adjust);
25823         
25824         this.execCmd('FontSize', v  );
25825     },
25826
25827     onEditorEvent : function(e){
25828         this.fireEvent('editorevent', this, e);
25829       //  this.updateToolbar();
25830         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
25831     },
25832
25833     insertTag : function(tg)
25834     {
25835         // could be a bit smarter... -> wrap the current selected tRoo..
25836         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
25837             
25838             range = this.createRange(this.getSelection());
25839             var wrappingNode = this.doc.createElement(tg.toLowerCase());
25840             wrappingNode.appendChild(range.extractContents());
25841             range.insertNode(wrappingNode);
25842
25843             return;
25844             
25845             
25846             
25847         }
25848         this.execCmd("formatblock",   tg);
25849         
25850     },
25851     
25852     insertText : function(txt)
25853     {
25854         
25855         
25856         var range = this.createRange();
25857         range.deleteContents();
25858                //alert(Sender.getAttribute('label'));
25859                
25860         range.insertNode(this.doc.createTextNode(txt));
25861     } ,
25862     
25863     // private
25864     relayBtnCmd : function(btn){
25865         this.relayCmd(btn.cmd);
25866     },
25867
25868     /**
25869      * Executes a Midas editor command on the editor document and performs necessary focus and
25870      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25871      * @param {String} cmd The Midas command
25872      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25873      */
25874     relayCmd : function(cmd, value){
25875         this.win.focus();
25876         this.execCmd(cmd, value);
25877         this.fireEvent('editorevent', this);
25878         //this.updateToolbar();
25879         this.deferFocus();
25880     },
25881
25882     /**
25883      * Executes a Midas editor command directly on the editor document.
25884      * For visual commands, you should use {@link #relayCmd} instead.
25885      * <b>This should only be called after the editor is initialized.</b>
25886      * @param {String} cmd The Midas command
25887      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25888      */
25889     execCmd : function(cmd, value){
25890         this.doc.execCommand(cmd, false, value === undefined ? null : value);
25891         this.syncValue();
25892     },
25893  
25894  
25895    
25896     /**
25897      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25898      * to insert tRoo.
25899      * @param {String} text | dom node.. 
25900      */
25901     insertAtCursor : function(text)
25902     {
25903         
25904         
25905         
25906         if(!this.activated){
25907             return;
25908         }
25909         /*
25910         if(Roo.isIE){
25911             this.win.focus();
25912             var r = this.doc.selection.createRange();
25913             if(r){
25914                 r.collapse(true);
25915                 r.pasteHTML(text);
25916                 this.syncValue();
25917                 this.deferFocus();
25918             
25919             }
25920             return;
25921         }
25922         */
25923         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25924             this.win.focus();
25925             
25926             
25927             // from jquery ui (MIT licenced)
25928             var range, node;
25929             var win = this.win;
25930             
25931             if (win.getSelection && win.getSelection().getRangeAt) {
25932                 range = win.getSelection().getRangeAt(0);
25933                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25934                 range.insertNode(node);
25935             } else if (win.document.selection && win.document.selection.createRange) {
25936                 // no firefox support
25937                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25938                 win.document.selection.createRange().pasteHTML(txt);
25939             } else {
25940                 // no firefox support
25941                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25942                 this.execCmd('InsertHTML', txt);
25943             } 
25944             
25945             this.syncValue();
25946             
25947             this.deferFocus();
25948         }
25949     },
25950  // private
25951     mozKeyPress : function(e){
25952         if(e.ctrlKey){
25953             var c = e.getCharCode(), cmd;
25954           
25955             if(c > 0){
25956                 c = String.fromCharCode(c).toLowerCase();
25957                 switch(c){
25958                     case 'b':
25959                         cmd = 'bold';
25960                         break;
25961                     case 'i':
25962                         cmd = 'italic';
25963                         break;
25964                     
25965                     case 'u':
25966                         cmd = 'underline';
25967                         break;
25968                     
25969                     case 'v':
25970                         this.cleanUpPaste.defer(100, this);
25971                         return;
25972                         
25973                 }
25974                 if(cmd){
25975                     this.win.focus();
25976                     this.execCmd(cmd);
25977                     this.deferFocus();
25978                     e.preventDefault();
25979                 }
25980                 
25981             }
25982         }
25983     },
25984
25985     // private
25986     fixKeys : function(){ // load time branching for fastest keydown performance
25987         if(Roo.isIE){
25988             return function(e){
25989                 var k = e.getKey(), r;
25990                 if(k == e.TAB){
25991                     e.stopEvent();
25992                     r = this.doc.selection.createRange();
25993                     if(r){
25994                         r.collapse(true);
25995                         r.pasteHTML('&#160;&#160;&#160;&#160;');
25996                         this.deferFocus();
25997                     }
25998                     return;
25999                 }
26000                 
26001                 if(k == e.ENTER){
26002                     r = this.doc.selection.createRange();
26003                     if(r){
26004                         var target = r.parentElement();
26005                         if(!target || target.tagName.toLowerCase() != 'li'){
26006                             e.stopEvent();
26007                             r.pasteHTML('<br />');
26008                             r.collapse(false);
26009                             r.select();
26010                         }
26011                     }
26012                 }
26013                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26014                     this.cleanUpPaste.defer(100, this);
26015                     return;
26016                 }
26017                 
26018                 
26019             };
26020         }else if(Roo.isOpera){
26021             return function(e){
26022                 var k = e.getKey();
26023                 if(k == e.TAB){
26024                     e.stopEvent();
26025                     this.win.focus();
26026                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
26027                     this.deferFocus();
26028                 }
26029                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26030                     this.cleanUpPaste.defer(100, this);
26031                     return;
26032                 }
26033                 
26034             };
26035         }else if(Roo.isSafari){
26036             return function(e){
26037                 var k = e.getKey();
26038                 
26039                 if(k == e.TAB){
26040                     e.stopEvent();
26041                     this.execCmd('InsertText','\t');
26042                     this.deferFocus();
26043                     return;
26044                 }
26045                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26046                     this.cleanUpPaste.defer(100, this);
26047                     return;
26048                 }
26049                 
26050              };
26051         }
26052     }(),
26053     
26054     getAllAncestors: function()
26055     {
26056         var p = this.getSelectedNode();
26057         var a = [];
26058         if (!p) {
26059             a.push(p); // push blank onto stack..
26060             p = this.getParentElement();
26061         }
26062         
26063         
26064         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26065             a.push(p);
26066             p = p.parentNode;
26067         }
26068         a.push(this.doc.body);
26069         return a;
26070     },
26071     lastSel : false,
26072     lastSelNode : false,
26073     
26074     
26075     getSelection : function() 
26076     {
26077         this.assignDocWin();
26078         return Roo.isIE ? this.doc.selection : this.win.getSelection();
26079     },
26080     
26081     getSelectedNode: function() 
26082     {
26083         // this may only work on Gecko!!!
26084         
26085         // should we cache this!!!!
26086         
26087         
26088         
26089          
26090         var range = this.createRange(this.getSelection()).cloneRange();
26091         
26092         if (Roo.isIE) {
26093             var parent = range.parentElement();
26094             while (true) {
26095                 var testRange = range.duplicate();
26096                 testRange.moveToElementText(parent);
26097                 if (testRange.inRange(range)) {
26098                     break;
26099                 }
26100                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26101                     break;
26102                 }
26103                 parent = parent.parentElement;
26104             }
26105             return parent;
26106         }
26107         
26108         // is ancestor a text element.
26109         var ac =  range.commonAncestorContainer;
26110         if (ac.nodeType == 3) {
26111             ac = ac.parentNode;
26112         }
26113         
26114         var ar = ac.childNodes;
26115          
26116         var nodes = [];
26117         var other_nodes = [];
26118         var has_other_nodes = false;
26119         for (var i=0;i<ar.length;i++) {
26120             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
26121                 continue;
26122             }
26123             // fullly contained node.
26124             
26125             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26126                 nodes.push(ar[i]);
26127                 continue;
26128             }
26129             
26130             // probably selected..
26131             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26132                 other_nodes.push(ar[i]);
26133                 continue;
26134             }
26135             // outer..
26136             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
26137                 continue;
26138             }
26139             
26140             
26141             has_other_nodes = true;
26142         }
26143         if (!nodes.length && other_nodes.length) {
26144             nodes= other_nodes;
26145         }
26146         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26147             return false;
26148         }
26149         
26150         return nodes[0];
26151     },
26152     createRange: function(sel)
26153     {
26154         // this has strange effects when using with 
26155         // top toolbar - not sure if it's a great idea.
26156         //this.editor.contentWindow.focus();
26157         if (typeof sel != "undefined") {
26158             try {
26159                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26160             } catch(e) {
26161                 return this.doc.createRange();
26162             }
26163         } else {
26164             return this.doc.createRange();
26165         }
26166     },
26167     getParentElement: function()
26168     {
26169         
26170         this.assignDocWin();
26171         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26172         
26173         var range = this.createRange(sel);
26174          
26175         try {
26176             var p = range.commonAncestorContainer;
26177             while (p.nodeType == 3) { // text node
26178                 p = p.parentNode;
26179             }
26180             return p;
26181         } catch (e) {
26182             return null;
26183         }
26184     
26185     },
26186     /***
26187      *
26188      * Range intersection.. the hard stuff...
26189      *  '-1' = before
26190      *  '0' = hits..
26191      *  '1' = after.
26192      *         [ -- selected range --- ]
26193      *   [fail]                        [fail]
26194      *
26195      *    basically..
26196      *      if end is before start or  hits it. fail.
26197      *      if start is after end or hits it fail.
26198      *
26199      *   if either hits (but other is outside. - then it's not 
26200      *   
26201      *    
26202      **/
26203     
26204     
26205     // @see http://www.thismuchiknow.co.uk/?p=64.
26206     rangeIntersectsNode : function(range, node)
26207     {
26208         var nodeRange = node.ownerDocument.createRange();
26209         try {
26210             nodeRange.selectNode(node);
26211         } catch (e) {
26212             nodeRange.selectNodeContents(node);
26213         }
26214     
26215         var rangeStartRange = range.cloneRange();
26216         rangeStartRange.collapse(true);
26217     
26218         var rangeEndRange = range.cloneRange();
26219         rangeEndRange.collapse(false);
26220     
26221         var nodeStartRange = nodeRange.cloneRange();
26222         nodeStartRange.collapse(true);
26223     
26224         var nodeEndRange = nodeRange.cloneRange();
26225         nodeEndRange.collapse(false);
26226     
26227         return rangeStartRange.compareBoundaryPoints(
26228                  Range.START_TO_START, nodeEndRange) == -1 &&
26229                rangeEndRange.compareBoundaryPoints(
26230                  Range.START_TO_START, nodeStartRange) == 1;
26231         
26232          
26233     },
26234     rangeCompareNode : function(range, node)
26235     {
26236         var nodeRange = node.ownerDocument.createRange();
26237         try {
26238             nodeRange.selectNode(node);
26239         } catch (e) {
26240             nodeRange.selectNodeContents(node);
26241         }
26242         
26243         
26244         range.collapse(true);
26245     
26246         nodeRange.collapse(true);
26247      
26248         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26249         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
26250          
26251         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26252         
26253         var nodeIsBefore   =  ss == 1;
26254         var nodeIsAfter    = ee == -1;
26255         
26256         if (nodeIsBefore && nodeIsAfter)
26257             return 0; // outer
26258         if (!nodeIsBefore && nodeIsAfter)
26259             return 1; //right trailed.
26260         
26261         if (nodeIsBefore && !nodeIsAfter)
26262             return 2;  // left trailed.
26263         // fully contined.
26264         return 3;
26265     },
26266
26267     // private? - in a new class?
26268     cleanUpPaste :  function()
26269     {
26270         // cleans up the whole document..
26271          Roo.log('cleanuppaste');
26272         this.cleanUpChildren(this.doc.body);
26273         var clean = this.cleanWordChars(this.doc.body.innerHTML);
26274         if (clean != this.doc.body.innerHTML) {
26275             this.doc.body.innerHTML = clean;
26276         }
26277         
26278     },
26279     
26280     cleanWordChars : function(input) {// change the chars to hex code
26281         var he = Roo.form.HtmlEditor;
26282         
26283         var output = input;
26284         Roo.each(he.swapCodes, function(sw) { 
26285             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26286             
26287             output = output.replace(swapper, sw[1]);
26288         });
26289         
26290         return output;
26291     },
26292     
26293     
26294     cleanUpChildren : function (n)
26295     {
26296         if (!n.childNodes.length) {
26297             return;
26298         }
26299         for (var i = n.childNodes.length-1; i > -1 ; i--) {
26300            this.cleanUpChild(n.childNodes[i]);
26301         }
26302     },
26303     
26304     
26305         
26306     
26307     cleanUpChild : function (node)
26308     {
26309         var ed = this;
26310         //console.log(node);
26311         if (node.nodeName == "#text") {
26312             // clean up silly Windows -- stuff?
26313             return; 
26314         }
26315         if (node.nodeName == "#comment") {
26316             node.parentNode.removeChild(node);
26317             // clean up silly Windows -- stuff?
26318             return; 
26319         }
26320         
26321         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
26322             // remove node.
26323             node.parentNode.removeChild(node);
26324             return;
26325             
26326         }
26327         
26328         var remove_keep_children= Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1;
26329         
26330         // remove <a name=....> as rendering on yahoo mailer is borked with this.
26331         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26332         
26333         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26334         //    remove_keep_children = true;
26335         //}
26336         
26337         if (remove_keep_children) {
26338             this.cleanUpChildren(node);
26339             // inserts everything just before this node...
26340             while (node.childNodes.length) {
26341                 var cn = node.childNodes[0];
26342                 node.removeChild(cn);
26343                 node.parentNode.insertBefore(cn, node);
26344             }
26345             node.parentNode.removeChild(node);
26346             return;
26347         }
26348         
26349         if (!node.attributes || !node.attributes.length) {
26350             this.cleanUpChildren(node);
26351             return;
26352         }
26353         
26354         function cleanAttr(n,v)
26355         {
26356             
26357             if (v.match(/^\./) || v.match(/^\//)) {
26358                 return;
26359             }
26360             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
26361                 return;
26362             }
26363             if (v.match(/^#/)) {
26364                 return;
26365             }
26366 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26367             node.removeAttribute(n);
26368             
26369         }
26370         
26371         function cleanStyle(n,v)
26372         {
26373             if (v.match(/expression/)) { //XSS?? should we even bother..
26374                 node.removeAttribute(n);
26375                 return;
26376             }
26377             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.form.HtmlEditor.cwhite : ed.cwhite;
26378             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.form.HtmlEditor.cblack : ed.cblack;
26379             
26380             
26381             var parts = v.split(/;/);
26382             var clean = [];
26383             
26384             Roo.each(parts, function(p) {
26385                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26386                 if (!p.length) {
26387                     return true;
26388                 }
26389                 var l = p.split(':').shift().replace(/\s+/g,'');
26390                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26391                 
26392                 
26393                 if ( cblack.indexOf(l) > -1) {
26394 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26395                     //node.removeAttribute(n);
26396                     return true;
26397                 }
26398                 //Roo.log()
26399                 // only allow 'c whitelisted system attributes'
26400                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
26401 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26402                     //node.removeAttribute(n);
26403                     return true;
26404                 }
26405                 
26406                 
26407                  
26408                 
26409                 clean.push(p);
26410                 return true;
26411             });
26412             if (clean.length) { 
26413                 node.setAttribute(n, clean.join(';'));
26414             } else {
26415                 node.removeAttribute(n);
26416             }
26417             
26418         }
26419         
26420         
26421         for (var i = node.attributes.length-1; i > -1 ; i--) {
26422             var a = node.attributes[i];
26423             //console.log(a);
26424             
26425             if (a.name.toLowerCase().substr(0,2)=='on')  {
26426                 node.removeAttribute(a.name);
26427                 continue;
26428             }
26429             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
26430                 node.removeAttribute(a.name);
26431                 continue;
26432             }
26433             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
26434                 cleanAttr(a.name,a.value); // fixme..
26435                 continue;
26436             }
26437             if (a.name == 'style') {
26438                 cleanStyle(a.name,a.value);
26439                 continue;
26440             }
26441             /// clean up MS crap..
26442             // tecnically this should be a list of valid class'es..
26443             
26444             
26445             if (a.name == 'class') {
26446                 if (a.value.match(/^Mso/)) {
26447                     node.className = '';
26448                 }
26449                 
26450                 if (a.value.match(/body/)) {
26451                     node.className = '';
26452                 }
26453                 continue;
26454             }
26455             
26456             // style cleanup!?
26457             // class cleanup?
26458             
26459         }
26460         
26461         
26462         this.cleanUpChildren(node);
26463         
26464         
26465     }
26466     
26467     
26468     // hide stuff that is not compatible
26469     /**
26470      * @event blur
26471      * @hide
26472      */
26473     /**
26474      * @event change
26475      * @hide
26476      */
26477     /**
26478      * @event focus
26479      * @hide
26480      */
26481     /**
26482      * @event specialkey
26483      * @hide
26484      */
26485     /**
26486      * @cfg {String} fieldClass @hide
26487      */
26488     /**
26489      * @cfg {String} focusClass @hide
26490      */
26491     /**
26492      * @cfg {String} autoCreate @hide
26493      */
26494     /**
26495      * @cfg {String} inputType @hide
26496      */
26497     /**
26498      * @cfg {String} invalidClass @hide
26499      */
26500     /**
26501      * @cfg {String} invalidText @hide
26502      */
26503     /**
26504      * @cfg {String} msgFx @hide
26505      */
26506     /**
26507      * @cfg {String} validateOnBlur @hide
26508      */
26509 });
26510
26511 Roo.form.HtmlEditor.white = [
26512         'area', 'br', 'img', 'input', 'hr', 'wbr',
26513         
26514        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
26515        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
26516        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
26517        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
26518        'table',   'ul',         'xmp', 
26519        
26520        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
26521       'thead',   'tr', 
26522      
26523       'dir', 'menu', 'ol', 'ul', 'dl',
26524        
26525       'embed',  'object'
26526 ];
26527
26528
26529 Roo.form.HtmlEditor.black = [
26530     //    'embed',  'object', // enable - backend responsiblity to clean thiese
26531         'applet', // 
26532         'base',   'basefont', 'bgsound', 'blink',  'body', 
26533         'frame',  'frameset', 'head',    'html',   'ilayer', 
26534         'iframe', 'layer',  'link',     'meta',    'object',   
26535         'script', 'style' ,'title',  'xml' // clean later..
26536 ];
26537 Roo.form.HtmlEditor.clean = [
26538     'script', 'style', 'title', 'xml'
26539 ];
26540 Roo.form.HtmlEditor.remove = [
26541     'font'
26542 ];
26543 // attributes..
26544
26545 Roo.form.HtmlEditor.ablack = [
26546     'on'
26547 ];
26548     
26549 Roo.form.HtmlEditor.aclean = [ 
26550     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
26551 ];
26552
26553 // protocols..
26554 Roo.form.HtmlEditor.pwhite= [
26555         'http',  'https',  'mailto'
26556 ];
26557
26558 // white listed style attributes.
26559 Roo.form.HtmlEditor.cwhite= [
26560       //  'text-align', /// default is to allow most things..
26561       
26562          
26563 //        'font-size'//??
26564 ];
26565
26566 // black listed style attributes.
26567 Roo.form.HtmlEditor.cblack= [
26568       //  'font-size' -- this can be set by the project 
26569 ];
26570
26571
26572 Roo.form.HtmlEditor.swapCodes   =[ 
26573     [    8211, "--" ], 
26574     [    8212, "--" ], 
26575     [    8216,  "'" ],  
26576     [    8217, "'" ],  
26577     [    8220, '"' ],  
26578     [    8221, '"' ],  
26579     [    8226, "*" ],  
26580     [    8230, "..." ]
26581 ]; 
26582
26583     // <script type="text/javascript">
26584 /*
26585  * Based on
26586  * Ext JS Library 1.1.1
26587  * Copyright(c) 2006-2007, Ext JS, LLC.
26588  *  
26589  
26590  */
26591
26592 /**
26593  * @class Roo.form.HtmlEditorToolbar1
26594  * Basic Toolbar
26595  * 
26596  * Usage:
26597  *
26598  new Roo.form.HtmlEditor({
26599     ....
26600     toolbars : [
26601         new Roo.form.HtmlEditorToolbar1({
26602             disable : { fonts: 1 , format: 1, ..., ... , ...],
26603             btns : [ .... ]
26604         })
26605     }
26606      
26607  * 
26608  * @cfg {Object} disable List of elements to disable..
26609  * @cfg {Array} btns List of additional buttons.
26610  * 
26611  * 
26612  * NEEDS Extra CSS? 
26613  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26614  */
26615  
26616 Roo.form.HtmlEditor.ToolbarStandard = function(config)
26617 {
26618     
26619     Roo.apply(this, config);
26620     
26621     // default disabled, based on 'good practice'..
26622     this.disable = this.disable || {};
26623     Roo.applyIf(this.disable, {
26624         fontSize : true,
26625         colors : true,
26626         specialElements : true
26627     });
26628     
26629     
26630     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26631     // dont call parent... till later.
26632 }
26633
26634 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
26635     
26636     tb: false,
26637     
26638     rendered: false,
26639     
26640     editor : false,
26641     /**
26642      * @cfg {Object} disable  List of toolbar elements to disable
26643          
26644      */
26645     disable : false,
26646       /**
26647      * @cfg {Array} fontFamilies An array of available font families
26648      */
26649     fontFamilies : [
26650         'Arial',
26651         'Courier New',
26652         'Tahoma',
26653         'Times New Roman',
26654         'Verdana'
26655     ],
26656     
26657     specialChars : [
26658            "&#169;",
26659           "&#174;",     
26660           "&#8482;",    
26661           "&#163;" ,    
26662          // "&#8212;",    
26663           "&#8230;",    
26664           "&#247;" ,    
26665         //  "&#225;" ,     ?? a acute?
26666            "&#8364;"    , //Euro
26667        //   "&#8220;"    ,
26668         //  "&#8221;"    ,
26669         //  "&#8226;"    ,
26670           "&#176;"  //   , // degrees
26671
26672          // "&#233;"     , // e ecute
26673          // "&#250;"     , // u ecute?
26674     ],
26675     
26676     specialElements : [
26677         {
26678             text: "Insert Table",
26679             xtype: 'MenuItem',
26680             xns : Roo.Menu,
26681             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
26682                 
26683         },
26684         {    
26685             text: "Insert Image",
26686             xtype: 'MenuItem',
26687             xns : Roo.Menu,
26688             ihtml : '<img src="about:blank"/>'
26689             
26690         }
26691         
26692          
26693     ],
26694     
26695     
26696     inputElements : [ 
26697             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
26698             "input:submit", "input:button", "select", "textarea", "label" ],
26699     formats : [
26700         ["p"] ,  
26701         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
26702         ["pre"],[ "code"], 
26703         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
26704         ['div'],['span']
26705     ],
26706      /**
26707      * @cfg {String} defaultFont default font to use.
26708      */
26709     defaultFont: 'tahoma',
26710    
26711     fontSelect : false,
26712     
26713     
26714     formatCombo : false,
26715     
26716     init : function(editor)
26717     {
26718         this.editor = editor;
26719         
26720         
26721         var fid = editor.frameId;
26722         var etb = this;
26723         function btn(id, toggle, handler){
26724             var xid = fid + '-'+ id ;
26725             return {
26726                 id : xid,
26727                 cmd : id,
26728                 cls : 'x-btn-icon x-edit-'+id,
26729                 enableToggle:toggle !== false,
26730                 scope: editor, // was editor...
26731                 handler:handler||editor.relayBtnCmd,
26732                 clickEvent:'mousedown',
26733                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
26734                 tabIndex:-1
26735             };
26736         }
26737         
26738         
26739         
26740         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
26741         this.tb = tb;
26742          // stop form submits
26743         tb.el.on('click', function(e){
26744             e.preventDefault(); // what does this do?
26745         });
26746
26747         if(!this.disable.font) { // && !Roo.isSafari){
26748             /* why no safari for fonts 
26749             editor.fontSelect = tb.el.createChild({
26750                 tag:'select',
26751                 tabIndex: -1,
26752                 cls:'x-font-select',
26753                 html: this.createFontOptions()
26754             });
26755             
26756             editor.fontSelect.on('change', function(){
26757                 var font = editor.fontSelect.dom.value;
26758                 editor.relayCmd('fontname', font);
26759                 editor.deferFocus();
26760             }, editor);
26761             
26762             tb.add(
26763                 editor.fontSelect.dom,
26764                 '-'
26765             );
26766             */
26767             
26768         };
26769         if(!this.disable.formats){
26770             this.formatCombo = new Roo.form.ComboBox({
26771                 store: new Roo.data.SimpleStore({
26772                     id : 'tag',
26773                     fields: ['tag'],
26774                     data : this.formats // from states.js
26775                 }),
26776                 blockFocus : true,
26777                 name : '',
26778                 //autoCreate : {tag: "div",  size: "20"},
26779                 displayField:'tag',
26780                 typeAhead: false,
26781                 mode: 'local',
26782                 editable : false,
26783                 triggerAction: 'all',
26784                 emptyText:'Add tag',
26785                 selectOnFocus:true,
26786                 width:135,
26787                 listeners : {
26788                     'select': function(c, r, i) {
26789                         editor.insertTag(r.get('tag'));
26790                         editor.focus();
26791                     }
26792                 }
26793
26794             });
26795             tb.addField(this.formatCombo);
26796             
26797         }
26798         
26799         if(!this.disable.format){
26800             tb.add(
26801                 btn('bold'),
26802                 btn('italic'),
26803                 btn('underline')
26804             );
26805         };
26806         if(!this.disable.fontSize){
26807             tb.add(
26808                 '-',
26809                 
26810                 
26811                 btn('increasefontsize', false, editor.adjustFont),
26812                 btn('decreasefontsize', false, editor.adjustFont)
26813             );
26814         };
26815         
26816         
26817         if(!this.disable.colors){
26818             tb.add(
26819                 '-', {
26820                     id:editor.frameId +'-forecolor',
26821                     cls:'x-btn-icon x-edit-forecolor',
26822                     clickEvent:'mousedown',
26823                     tooltip: this.buttonTips['forecolor'] || undefined,
26824                     tabIndex:-1,
26825                     menu : new Roo.menu.ColorMenu({
26826                         allowReselect: true,
26827                         focus: Roo.emptyFn,
26828                         value:'000000',
26829                         plain:true,
26830                         selectHandler: function(cp, color){
26831                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
26832                             editor.deferFocus();
26833                         },
26834                         scope: editor,
26835                         clickEvent:'mousedown'
26836                     })
26837                 }, {
26838                     id:editor.frameId +'backcolor',
26839                     cls:'x-btn-icon x-edit-backcolor',
26840                     clickEvent:'mousedown',
26841                     tooltip: this.buttonTips['backcolor'] || undefined,
26842                     tabIndex:-1,
26843                     menu : new Roo.menu.ColorMenu({
26844                         focus: Roo.emptyFn,
26845                         value:'FFFFFF',
26846                         plain:true,
26847                         allowReselect: true,
26848                         selectHandler: function(cp, color){
26849                             if(Roo.isGecko){
26850                                 editor.execCmd('useCSS', false);
26851                                 editor.execCmd('hilitecolor', color);
26852                                 editor.execCmd('useCSS', true);
26853                                 editor.deferFocus();
26854                             }else{
26855                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
26856                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
26857                                 editor.deferFocus();
26858                             }
26859                         },
26860                         scope:editor,
26861                         clickEvent:'mousedown'
26862                     })
26863                 }
26864             );
26865         };
26866         // now add all the items...
26867         
26868
26869         if(!this.disable.alignments){
26870             tb.add(
26871                 '-',
26872                 btn('justifyleft'),
26873                 btn('justifycenter'),
26874                 btn('justifyright')
26875             );
26876         };
26877
26878         //if(!Roo.isSafari){
26879             if(!this.disable.links){
26880                 tb.add(
26881                     '-',
26882                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
26883                 );
26884             };
26885
26886             if(!this.disable.lists){
26887                 tb.add(
26888                     '-',
26889                     btn('insertorderedlist'),
26890                     btn('insertunorderedlist')
26891                 );
26892             }
26893             if(!this.disable.sourceEdit){
26894                 tb.add(
26895                     '-',
26896                     btn('sourceedit', true, function(btn){
26897                         this.toggleSourceEdit(btn.pressed);
26898                     })
26899                 );
26900             }
26901         //}
26902         
26903         var smenu = { };
26904         // special menu.. - needs to be tidied up..
26905         if (!this.disable.special) {
26906             smenu = {
26907                 text: "&#169;",
26908                 cls: 'x-edit-none',
26909                 
26910                 menu : {
26911                     items : []
26912                 }
26913             };
26914             for (var i =0; i < this.specialChars.length; i++) {
26915                 smenu.menu.items.push({
26916                     
26917                     html: this.specialChars[i],
26918                     handler: function(a,b) {
26919                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
26920                         //editor.insertAtCursor(a.html);
26921                         
26922                     },
26923                     tabIndex:-1
26924                 });
26925             }
26926             
26927             
26928             tb.add(smenu);
26929             
26930             
26931         }
26932          
26933         if (!this.disable.specialElements) {
26934             var semenu = {
26935                 text: "Other;",
26936                 cls: 'x-edit-none',
26937                 menu : {
26938                     items : []
26939                 }
26940             };
26941             for (var i =0; i < this.specialElements.length; i++) {
26942                 semenu.menu.items.push(
26943                     Roo.apply({ 
26944                         handler: function(a,b) {
26945                             editor.insertAtCursor(this.ihtml);
26946                         }
26947                     }, this.specialElements[i])
26948                 );
26949                     
26950             }
26951             
26952             tb.add(semenu);
26953             
26954             
26955         }
26956          
26957         
26958         if (this.btns) {
26959             for(var i =0; i< this.btns.length;i++) {
26960                 var b = Roo.factory(this.btns[i],Roo.form);
26961                 b.cls =  'x-edit-none';
26962                 b.scope = editor;
26963                 tb.add(b);
26964             }
26965         
26966         }
26967         
26968         
26969         
26970         // disable everything...
26971         
26972         this.tb.items.each(function(item){
26973            if(item.id != editor.frameId+ '-sourceedit'){
26974                 item.disable();
26975             }
26976         });
26977         this.rendered = true;
26978         
26979         // the all the btns;
26980         editor.on('editorevent', this.updateToolbar, this);
26981         // other toolbars need to implement this..
26982         //editor.on('editmodechange', this.updateToolbar, this);
26983     },
26984     
26985     
26986     
26987     /**
26988      * Protected method that will not generally be called directly. It triggers
26989      * a toolbar update by reading the markup state of the current selection in the editor.
26990      */
26991     updateToolbar: function(){
26992
26993         if(!this.editor.activated){
26994             this.editor.onFirstFocus();
26995             return;
26996         }
26997
26998         var btns = this.tb.items.map, 
26999             doc = this.editor.doc,
27000             frameId = this.editor.frameId;
27001
27002         if(!this.disable.font && !Roo.isSafari){
27003             /*
27004             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
27005             if(name != this.fontSelect.dom.value){
27006                 this.fontSelect.dom.value = name;
27007             }
27008             */
27009         }
27010         if(!this.disable.format){
27011             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
27012             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
27013             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
27014         }
27015         if(!this.disable.alignments){
27016             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
27017             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
27018             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
27019         }
27020         if(!Roo.isSafari && !this.disable.lists){
27021             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
27022             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
27023         }
27024         
27025         var ans = this.editor.getAllAncestors();
27026         if (this.formatCombo) {
27027             
27028             
27029             var store = this.formatCombo.store;
27030             this.formatCombo.setValue("");
27031             for (var i =0; i < ans.length;i++) {
27032                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27033                     // select it..
27034                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27035                     break;
27036                 }
27037             }
27038         }
27039         
27040         
27041         
27042         // hides menus... - so this cant be on a menu...
27043         Roo.menu.MenuMgr.hideAll();
27044
27045         //this.editorsyncValue();
27046     },
27047    
27048     
27049     createFontOptions : function(){
27050         var buf = [], fs = this.fontFamilies, ff, lc;
27051         
27052         
27053         
27054         for(var i = 0, len = fs.length; i< len; i++){
27055             ff = fs[i];
27056             lc = ff.toLowerCase();
27057             buf.push(
27058                 '<option value="',lc,'" style="font-family:',ff,';"',
27059                     (this.defaultFont == lc ? ' selected="true">' : '>'),
27060                     ff,
27061                 '</option>'
27062             );
27063         }
27064         return buf.join('');
27065     },
27066     
27067     toggleSourceEdit : function(sourceEditMode){
27068         if(sourceEditMode === undefined){
27069             sourceEditMode = !this.sourceEditMode;
27070         }
27071         this.sourceEditMode = sourceEditMode === true;
27072         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
27073         // just toggle the button?
27074         if(btn.pressed !== this.editor.sourceEditMode){
27075             btn.toggle(this.editor.sourceEditMode);
27076             return;
27077         }
27078         
27079         if(this.sourceEditMode){
27080             this.tb.items.each(function(item){
27081                 if(item.cmd != 'sourceedit'){
27082                     item.disable();
27083                 }
27084             });
27085           
27086         }else{
27087             if(this.initialized){
27088                 this.tb.items.each(function(item){
27089                     item.enable();
27090                 });
27091             }
27092             
27093         }
27094         // tell the editor that it's been pressed..
27095         this.editor.toggleSourceEdit(sourceEditMode);
27096        
27097     },
27098      /**
27099      * Object collection of toolbar tooltips for the buttons in the editor. The key
27100      * is the command id associated with that button and the value is a valid QuickTips object.
27101      * For example:
27102 <pre><code>
27103 {
27104     bold : {
27105         title: 'Bold (Ctrl+B)',
27106         text: 'Make the selected text bold.',
27107         cls: 'x-html-editor-tip'
27108     },
27109     italic : {
27110         title: 'Italic (Ctrl+I)',
27111         text: 'Make the selected text italic.',
27112         cls: 'x-html-editor-tip'
27113     },
27114     ...
27115 </code></pre>
27116     * @type Object
27117      */
27118     buttonTips : {
27119         bold : {
27120             title: 'Bold (Ctrl+B)',
27121             text: 'Make the selected text bold.',
27122             cls: 'x-html-editor-tip'
27123         },
27124         italic : {
27125             title: 'Italic (Ctrl+I)',
27126             text: 'Make the selected text italic.',
27127             cls: 'x-html-editor-tip'
27128         },
27129         underline : {
27130             title: 'Underline (Ctrl+U)',
27131             text: 'Underline the selected text.',
27132             cls: 'x-html-editor-tip'
27133         },
27134         increasefontsize : {
27135             title: 'Grow Text',
27136             text: 'Increase the font size.',
27137             cls: 'x-html-editor-tip'
27138         },
27139         decreasefontsize : {
27140             title: 'Shrink Text',
27141             text: 'Decrease the font size.',
27142             cls: 'x-html-editor-tip'
27143         },
27144         backcolor : {
27145             title: 'Text Highlight Color',
27146             text: 'Change the background color of the selected text.',
27147             cls: 'x-html-editor-tip'
27148         },
27149         forecolor : {
27150             title: 'Font Color',
27151             text: 'Change the color of the selected text.',
27152             cls: 'x-html-editor-tip'
27153         },
27154         justifyleft : {
27155             title: 'Align Text Left',
27156             text: 'Align text to the left.',
27157             cls: 'x-html-editor-tip'
27158         },
27159         justifycenter : {
27160             title: 'Center Text',
27161             text: 'Center text in the editor.',
27162             cls: 'x-html-editor-tip'
27163         },
27164         justifyright : {
27165             title: 'Align Text Right',
27166             text: 'Align text to the right.',
27167             cls: 'x-html-editor-tip'
27168         },
27169         insertunorderedlist : {
27170             title: 'Bullet List',
27171             text: 'Start a bulleted list.',
27172             cls: 'x-html-editor-tip'
27173         },
27174         insertorderedlist : {
27175             title: 'Numbered List',
27176             text: 'Start a numbered list.',
27177             cls: 'x-html-editor-tip'
27178         },
27179         createlink : {
27180             title: 'Hyperlink',
27181             text: 'Make the selected text a hyperlink.',
27182             cls: 'x-html-editor-tip'
27183         },
27184         sourceedit : {
27185             title: 'Source Edit',
27186             text: 'Switch to source editing mode.',
27187             cls: 'x-html-editor-tip'
27188         }
27189     },
27190     // private
27191     onDestroy : function(){
27192         if(this.rendered){
27193             
27194             this.tb.items.each(function(item){
27195                 if(item.menu){
27196                     item.menu.removeAll();
27197                     if(item.menu.el){
27198                         item.menu.el.destroy();
27199                     }
27200                 }
27201                 item.destroy();
27202             });
27203              
27204         }
27205     },
27206     onFirstFocus: function() {
27207         this.tb.items.each(function(item){
27208            item.enable();
27209         });
27210     }
27211 });
27212
27213
27214
27215
27216 // <script type="text/javascript">
27217 /*
27218  * Based on
27219  * Ext JS Library 1.1.1
27220  * Copyright(c) 2006-2007, Ext JS, LLC.
27221  *  
27222  
27223  */
27224
27225  
27226 /**
27227  * @class Roo.form.HtmlEditor.ToolbarContext
27228  * Context Toolbar
27229  * 
27230  * Usage:
27231  *
27232  new Roo.form.HtmlEditor({
27233     ....
27234     toolbars : [
27235         { xtype: 'ToolbarStandard', styles : {} }
27236         { xtype: 'ToolbarContext', disable : {} }
27237     ]
27238 })
27239
27240      
27241  * 
27242  * @config : {Object} disable List of elements to disable.. (not done yet.)
27243  * @config : {Object} styles  Map of styles available.
27244  * 
27245  */
27246
27247 Roo.form.HtmlEditor.ToolbarContext = function(config)
27248 {
27249     
27250     Roo.apply(this, config);
27251     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27252     // dont call parent... till later.
27253     this.styles = this.styles || {};
27254 }
27255
27256  
27257
27258 Roo.form.HtmlEditor.ToolbarContext.types = {
27259     'IMG' : {
27260         width : {
27261             title: "Width",
27262             width: 40
27263         },
27264         height:  {
27265             title: "Height",
27266             width: 40
27267         },
27268         align: {
27269             title: "Align",
27270             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
27271             width : 80
27272             
27273         },
27274         border: {
27275             title: "Border",
27276             width: 40
27277         },
27278         alt: {
27279             title: "Alt",
27280             width: 120
27281         },
27282         src : {
27283             title: "Src",
27284             width: 220
27285         }
27286         
27287     },
27288     'A' : {
27289         name : {
27290             title: "Name",
27291             width: 50
27292         },
27293         href:  {
27294             title: "Href",
27295             width: 220
27296         } // border?
27297         
27298     },
27299     'TABLE' : {
27300         rows : {
27301             title: "Rows",
27302             width: 20
27303         },
27304         cols : {
27305             title: "Cols",
27306             width: 20
27307         },
27308         width : {
27309             title: "Width",
27310             width: 40
27311         },
27312         height : {
27313             title: "Height",
27314             width: 40
27315         },
27316         border : {
27317             title: "Border",
27318             width: 20
27319         }
27320     },
27321     'TD' : {
27322         width : {
27323             title: "Width",
27324             width: 40
27325         },
27326         height : {
27327             title: "Height",
27328             width: 40
27329         },   
27330         align: {
27331             title: "Align",
27332             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
27333             width: 80
27334         },
27335         valign: {
27336             title: "Valign",
27337             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
27338             width: 80
27339         },
27340         colspan: {
27341             title: "Colspan",
27342             width: 20
27343             
27344         },
27345          'font-family'  : {
27346             title : "Font",
27347             style : 'fontFamily',
27348             displayField: 'display',
27349             optname : 'font-family',
27350             width: 140
27351         }
27352     },
27353     'INPUT' : {
27354         name : {
27355             title: "name",
27356             width: 120
27357         },
27358         value : {
27359             title: "Value",
27360             width: 120
27361         },
27362         width : {
27363             title: "Width",
27364             width: 40
27365         }
27366     },
27367     'LABEL' : {
27368         'for' : {
27369             title: "For",
27370             width: 120
27371         }
27372     },
27373     'TEXTAREA' : {
27374           name : {
27375             title: "name",
27376             width: 120
27377         },
27378         rows : {
27379             title: "Rows",
27380             width: 20
27381         },
27382         cols : {
27383             title: "Cols",
27384             width: 20
27385         }
27386     },
27387     'SELECT' : {
27388         name : {
27389             title: "name",
27390             width: 120
27391         },
27392         selectoptions : {
27393             title: "Options",
27394             width: 200
27395         }
27396     },
27397     
27398     // should we really allow this??
27399     // should this just be 
27400     'BODY' : {
27401         title : {
27402             title: "Title",
27403             width: 200,
27404             disabled : true
27405         }
27406     },
27407     'SPAN' : {
27408         'font-family'  : {
27409             title : "Font",
27410             style : 'fontFamily',
27411             displayField: 'display',
27412             optname : 'font-family',
27413             width: 140
27414         }
27415     },
27416     'DIV' : {
27417         'font-family'  : {
27418             title : "Font",
27419             style : 'fontFamily',
27420             displayField: 'display',
27421             optname : 'font-family',
27422             width: 140
27423         }
27424     },
27425      'P' : {
27426         'font-family'  : {
27427             title : "Font",
27428             style : 'fontFamily',
27429             displayField: 'display',
27430             optname : 'font-family',
27431             width: 140
27432         }
27433     },
27434     
27435     '*' : {
27436         // empty..
27437     }
27438
27439 };
27440
27441 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
27442 Roo.form.HtmlEditor.ToolbarContext.stores = false;
27443
27444 Roo.form.HtmlEditor.ToolbarContext.options = {
27445         'font-family'  : [ 
27446                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
27447                 [ 'Courier New', 'Courier New'],
27448                 [ 'Tahoma', 'Tahoma'],
27449                 [ 'Times New Roman,serif', 'Times'],
27450                 [ 'Verdana','Verdana' ]
27451         ]
27452 };
27453
27454 // fixme - these need to be configurable..
27455  
27456
27457 Roo.form.HtmlEditor.ToolbarContext.types
27458
27459
27460 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
27461     
27462     tb: false,
27463     
27464     rendered: false,
27465     
27466     editor : false,
27467     /**
27468      * @cfg {Object} disable  List of toolbar elements to disable
27469          
27470      */
27471     disable : false,
27472     /**
27473      * @cfg {Object} styles List of styles 
27474      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
27475      *
27476      * These must be defined in the page, so they get rendered correctly..
27477      * .headline { }
27478      * TD.underline { }
27479      * 
27480      */
27481     styles : false,
27482     
27483     options: false,
27484     
27485     toolbars : false,
27486     
27487     init : function(editor)
27488     {
27489         this.editor = editor;
27490         
27491         
27492         var fid = editor.frameId;
27493         var etb = this;
27494         function btn(id, toggle, handler){
27495             var xid = fid + '-'+ id ;
27496             return {
27497                 id : xid,
27498                 cmd : id,
27499                 cls : 'x-btn-icon x-edit-'+id,
27500                 enableToggle:toggle !== false,
27501                 scope: editor, // was editor...
27502                 handler:handler||editor.relayBtnCmd,
27503                 clickEvent:'mousedown',
27504                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27505                 tabIndex:-1
27506             };
27507         }
27508         // create a new element.
27509         var wdiv = editor.wrap.createChild({
27510                 tag: 'div'
27511             }, editor.wrap.dom.firstChild.nextSibling, true);
27512         
27513         // can we do this more than once??
27514         
27515          // stop form submits
27516       
27517  
27518         // disable everything...
27519         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
27520         this.toolbars = {};
27521            
27522         for (var i in  ty) {
27523           
27524             this.toolbars[i] = this.buildToolbar(ty[i],i);
27525         }
27526         this.tb = this.toolbars.BODY;
27527         this.tb.el.show();
27528         this.buildFooter();
27529         this.footer.show();
27530         editor.on('hide', function( ) { this.footer.hide() }, this);
27531         editor.on('show', function( ) { this.footer.show() }, this);
27532         
27533          
27534         this.rendered = true;
27535         
27536         // the all the btns;
27537         editor.on('editorevent', this.updateToolbar, this);
27538         // other toolbars need to implement this..
27539         //editor.on('editmodechange', this.updateToolbar, this);
27540     },
27541     
27542     
27543     
27544     /**
27545      * Protected method that will not generally be called directly. It triggers
27546      * a toolbar update by reading the markup state of the current selection in the editor.
27547      */
27548     updateToolbar: function(editor,ev,sel){
27549
27550         //Roo.log(ev);
27551         // capture mouse up - this is handy for selecting images..
27552         // perhaps should go somewhere else...
27553         if(!this.editor.activated){
27554              this.editor.onFirstFocus();
27555             return;
27556         }
27557         
27558         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
27559         // selectNode - might want to handle IE?
27560         if (ev &&
27561             (ev.type == 'mouseup' || ev.type == 'click' ) &&
27562             ev.target && ev.target.tagName == 'IMG') {
27563             // they have click on an image...
27564             // let's see if we can change the selection...
27565             sel = ev.target;
27566          
27567               var nodeRange = sel.ownerDocument.createRange();
27568             try {
27569                 nodeRange.selectNode(sel);
27570             } catch (e) {
27571                 nodeRange.selectNodeContents(sel);
27572             }
27573             //nodeRange.collapse(true);
27574             var s = editor.win.getSelection();
27575             s.removeAllRanges();
27576             s.addRange(nodeRange);
27577         }  
27578         
27579       
27580         var updateFooter = sel ? false : true;
27581         
27582         
27583         var ans = this.editor.getAllAncestors();
27584         
27585         // pick
27586         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
27587         
27588         if (!sel) { 
27589             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
27590             sel = sel ? sel : this.editor.doc.body;
27591             sel = sel.tagName.length ? sel : this.editor.doc.body;
27592             
27593         }
27594         // pick a menu that exists..
27595         var tn = sel.tagName.toUpperCase();
27596         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
27597         
27598         tn = sel.tagName.toUpperCase();
27599         
27600         var lastSel = this.tb.selectedNode
27601         
27602         this.tb.selectedNode = sel;
27603         
27604         // if current menu does not match..
27605         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
27606                 
27607             this.tb.el.hide();
27608             ///console.log("show: " + tn);
27609             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
27610             this.tb.el.show();
27611             // update name
27612             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
27613             
27614             
27615             // update attributes
27616             if (this.tb.fields) {
27617                 this.tb.fields.each(function(e) {
27618                     if (e.stylename) {
27619                         e.setValue(sel.style[e.stylename]);
27620                         return;
27621                     } 
27622                    e.setValue(sel.getAttribute(e.attrname));
27623                 });
27624             }
27625             
27626             var hasStyles = false;
27627             for(var i in this.styles) {
27628                 hasStyles = true;
27629                 break;
27630             }
27631             
27632             // update styles
27633             if (hasStyles) { 
27634                 var st = this.tb.fields.item(0);
27635                 
27636                 st.store.removeAll();
27637                
27638                 
27639                 var cn = sel.className.split(/\s+/);
27640                 
27641                 var avs = [];
27642                 if (this.styles['*']) {
27643                     
27644                     Roo.each(this.styles['*'], function(v) {
27645                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
27646                     });
27647                 }
27648                 if (this.styles[tn]) { 
27649                     Roo.each(this.styles[tn], function(v) {
27650                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
27651                     });
27652                 }
27653                 
27654                 st.store.loadData(avs);
27655                 st.collapse();
27656                 st.setValue(cn);
27657             }
27658             // flag our selected Node.
27659             this.tb.selectedNode = sel;
27660            
27661            
27662             Roo.menu.MenuMgr.hideAll();
27663
27664         }
27665         
27666         if (!updateFooter) {
27667             //this.footDisp.dom.innerHTML = ''; 
27668             return;
27669         }
27670         // update the footer
27671         //
27672         var html = '';
27673         
27674         this.footerEls = ans.reverse();
27675         Roo.each(this.footerEls, function(a,i) {
27676             if (!a) { return; }
27677             html += html.length ? ' &gt; '  :  '';
27678             
27679             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
27680             
27681         });
27682        
27683         // 
27684         var sz = this.footDisp.up('td').getSize();
27685         this.footDisp.dom.style.width = (sz.width -10) + 'px';
27686         this.footDisp.dom.style.marginLeft = '5px';
27687         
27688         this.footDisp.dom.style.overflow = 'hidden';
27689         
27690         this.footDisp.dom.innerHTML = html;
27691             
27692         //this.editorsyncValue();
27693     },
27694      
27695     
27696    
27697        
27698     // private
27699     onDestroy : function(){
27700         if(this.rendered){
27701             
27702             this.tb.items.each(function(item){
27703                 if(item.menu){
27704                     item.menu.removeAll();
27705                     if(item.menu.el){
27706                         item.menu.el.destroy();
27707                     }
27708                 }
27709                 item.destroy();
27710             });
27711              
27712         }
27713     },
27714     onFirstFocus: function() {
27715         // need to do this for all the toolbars..
27716         this.tb.items.each(function(item){
27717            item.enable();
27718         });
27719     },
27720     buildToolbar: function(tlist, nm)
27721     {
27722         var editor = this.editor;
27723          // create a new element.
27724         var wdiv = editor.wrap.createChild({
27725                 tag: 'div'
27726             }, editor.wrap.dom.firstChild.nextSibling, true);
27727         
27728        
27729         var tb = new Roo.Toolbar(wdiv);
27730         // add the name..
27731         
27732         tb.add(nm+ ":&nbsp;");
27733         
27734         var styles = [];
27735         for(var i in this.styles) {
27736             styles.push(i);
27737         }
27738         
27739         // styles...
27740         if (styles && styles.length) {
27741             
27742             // this needs a multi-select checkbox...
27743             tb.addField( new Roo.form.ComboBox({
27744                 store: new Roo.data.SimpleStore({
27745                     id : 'val',
27746                     fields: ['val', 'selected'],
27747                     data : [] 
27748                 }),
27749                 name : '-roo-edit-className',
27750                 attrname : 'className',
27751                 displayField: 'val',
27752                 typeAhead: false,
27753                 mode: 'local',
27754                 editable : false,
27755                 triggerAction: 'all',
27756                 emptyText:'Select Style',
27757                 selectOnFocus:true,
27758                 width: 130,
27759                 listeners : {
27760                     'select': function(c, r, i) {
27761                         // initial support only for on class per el..
27762                         tb.selectedNode.className =  r ? r.get('val') : '';
27763                         editor.syncValue();
27764                     }
27765                 }
27766     
27767             }));
27768         }
27769         
27770         var tbc = Roo.form.HtmlEditor.ToolbarContext;
27771         var tbops = tbc.options;
27772         
27773         for (var i in tlist) {
27774             
27775             var item = tlist[i];
27776             tb.add(item.title + ":&nbsp;");
27777             
27778             
27779             //optname == used so you can configure the options available..
27780             var opts = item.opts ? item.opts : false;
27781             if (item.optname) {
27782                 opts = tbops[item.optname];
27783            
27784             }
27785             
27786             if (opts) {
27787                 // opts == pulldown..
27788                 tb.addField( new Roo.form.ComboBox({
27789                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
27790                         id : 'val',
27791                         fields: ['val', 'display'],
27792                         data : opts  
27793                     }),
27794                     name : '-roo-edit-' + i,
27795                     attrname : i,
27796                     stylename : item.style ? item.style : false,
27797                     displayField: item.displayField ? item.displayField : 'val',
27798                     valueField :  'val',
27799                     typeAhead: false,
27800                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
27801                     editable : false,
27802                     triggerAction: 'all',
27803                     emptyText:'Select',
27804                     selectOnFocus:true,
27805                     width: item.width ? item.width  : 130,
27806                     listeners : {
27807                         'select': function(c, r, i) {
27808                             if (c.stylename) {
27809                                 tb.selectedNode.style[c.stylename] =  r.get('val');
27810                                 return;
27811                             }
27812                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
27813                         }
27814                     }
27815
27816                 }));
27817                 continue;
27818                     
27819                  
27820                 
27821                 tb.addField( new Roo.form.TextField({
27822                     name: i,
27823                     width: 100,
27824                     //allowBlank:false,
27825                     value: ''
27826                 }));
27827                 continue;
27828             }
27829             tb.addField( new Roo.form.TextField({
27830                 name: '-roo-edit-' + i,
27831                 attrname : i,
27832                 
27833                 width: item.width,
27834                 //allowBlank:true,
27835                 value: '',
27836                 listeners: {
27837                     'change' : function(f, nv, ov) {
27838                         tb.selectedNode.setAttribute(f.attrname, nv);
27839                     }
27840                 }
27841             }));
27842              
27843         }
27844         tb.addFill();
27845         var _this = this;
27846         tb.addButton( {
27847             text: 'Remove Tag',
27848     
27849             listeners : {
27850                 click : function ()
27851                 {
27852                     // remove
27853                     // undo does not work.
27854                      
27855                     var sn = tb.selectedNode;
27856                     
27857                     var pn = sn.parentNode;
27858                     
27859                     var stn =  sn.childNodes[0];
27860                     var en = sn.childNodes[sn.childNodes.length - 1 ];
27861                     while (sn.childNodes.length) {
27862                         var node = sn.childNodes[0];
27863                         sn.removeChild(node);
27864                         //Roo.log(node);
27865                         pn.insertBefore(node, sn);
27866                         
27867                     }
27868                     pn.removeChild(sn);
27869                     var range = editor.createRange();
27870         
27871                     range.setStart(stn,0);
27872                     range.setEnd(en,0); //????
27873                     //range.selectNode(sel);
27874                     
27875                     
27876                     var selection = editor.getSelection();
27877                     selection.removeAllRanges();
27878                     selection.addRange(range);
27879                     
27880                     
27881                     
27882                     //_this.updateToolbar(null, null, pn);
27883                     _this.updateToolbar(null, null, null);
27884                     _this.footDisp.dom.innerHTML = ''; 
27885                 }
27886             }
27887             
27888                     
27889                 
27890             
27891         });
27892         
27893         
27894         tb.el.on('click', function(e){
27895             e.preventDefault(); // what does this do?
27896         });
27897         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
27898         tb.el.hide();
27899         tb.name = nm;
27900         // dont need to disable them... as they will get hidden
27901         return tb;
27902          
27903         
27904     },
27905     buildFooter : function()
27906     {
27907         
27908         var fel = this.editor.wrap.createChild();
27909         this.footer = new Roo.Toolbar(fel);
27910         // toolbar has scrolly on left / right?
27911         var footDisp= new Roo.Toolbar.Fill();
27912         var _t = this;
27913         this.footer.add(
27914             {
27915                 text : '&lt;',
27916                 xtype: 'Button',
27917                 handler : function() {
27918                     _t.footDisp.scrollTo('left',0,true)
27919                 }
27920             }
27921         );
27922         this.footer.add( footDisp );
27923         this.footer.add( 
27924             {
27925                 text : '&gt;',
27926                 xtype: 'Button',
27927                 handler : function() {
27928                     // no animation..
27929                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
27930                 }
27931             }
27932         );
27933         var fel = Roo.get(footDisp.el);
27934         fel.addClass('x-editor-context');
27935         this.footDispWrap = fel; 
27936         this.footDispWrap.overflow  = 'hidden';
27937         
27938         this.footDisp = fel.createChild();
27939         this.footDispWrap.on('click', this.onContextClick, this)
27940         
27941         
27942     },
27943     onContextClick : function (ev,dom)
27944     {
27945         ev.preventDefault();
27946         var  cn = dom.className;
27947         //Roo.log(cn);
27948         if (!cn.match(/x-ed-loc-/)) {
27949             return;
27950         }
27951         var n = cn.split('-').pop();
27952         var ans = this.footerEls;
27953         var sel = ans[n];
27954         
27955          // pick
27956         var range = this.editor.createRange();
27957         
27958         range.selectNodeContents(sel);
27959         //range.selectNode(sel);
27960         
27961         
27962         var selection = this.editor.getSelection();
27963         selection.removeAllRanges();
27964         selection.addRange(range);
27965         
27966         
27967         
27968         this.updateToolbar(null, null, sel);
27969         
27970         
27971     }
27972     
27973     
27974     
27975     
27976     
27977 });
27978
27979
27980
27981
27982
27983 /*
27984  * Based on:
27985  * Ext JS Library 1.1.1
27986  * Copyright(c) 2006-2007, Ext JS, LLC.
27987  *
27988  * Originally Released Under LGPL - original licence link has changed is not relivant.
27989  *
27990  * Fork - LGPL
27991  * <script type="text/javascript">
27992  */
27993  
27994 /**
27995  * @class Roo.form.BasicForm
27996  * @extends Roo.util.Observable
27997  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
27998  * @constructor
27999  * @param {String/HTMLElement/Roo.Element} el The form element or its id
28000  * @param {Object} config Configuration options
28001  */
28002 Roo.form.BasicForm = function(el, config){
28003     this.allItems = [];
28004     this.childForms = [];
28005     Roo.apply(this, config);
28006     /*
28007      * The Roo.form.Field items in this form.
28008      * @type MixedCollection
28009      */
28010      
28011      
28012     this.items = new Roo.util.MixedCollection(false, function(o){
28013         return o.id || (o.id = Roo.id());
28014     });
28015     this.addEvents({
28016         /**
28017          * @event beforeaction
28018          * Fires before any action is performed. Return false to cancel the action.
28019          * @param {Form} this
28020          * @param {Action} action The action to be performed
28021          */
28022         beforeaction: true,
28023         /**
28024          * @event actionfailed
28025          * Fires when an action fails.
28026          * @param {Form} this
28027          * @param {Action} action The action that failed
28028          */
28029         actionfailed : true,
28030         /**
28031          * @event actioncomplete
28032          * Fires when an action is completed.
28033          * @param {Form} this
28034          * @param {Action} action The action that completed
28035          */
28036         actioncomplete : true
28037     });
28038     if(el){
28039         this.initEl(el);
28040     }
28041     Roo.form.BasicForm.superclass.constructor.call(this);
28042 };
28043
28044 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
28045     /**
28046      * @cfg {String} method
28047      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
28048      */
28049     /**
28050      * @cfg {DataReader} reader
28051      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
28052      * This is optional as there is built-in support for processing JSON.
28053      */
28054     /**
28055      * @cfg {DataReader} errorReader
28056      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
28057      * This is completely optional as there is built-in support for processing JSON.
28058      */
28059     /**
28060      * @cfg {String} url
28061      * The URL to use for form actions if one isn't supplied in the action options.
28062      */
28063     /**
28064      * @cfg {Boolean} fileUpload
28065      * Set to true if this form is a file upload.
28066      */
28067      
28068     /**
28069      * @cfg {Object} baseParams
28070      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
28071      */
28072      /**
28073      
28074     /**
28075      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
28076      */
28077     timeout: 30,
28078
28079     // private
28080     activeAction : null,
28081
28082     /**
28083      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
28084      * or setValues() data instead of when the form was first created.
28085      */
28086     trackResetOnLoad : false,
28087     
28088     
28089     /**
28090      * childForms - used for multi-tab forms
28091      * @type {Array}
28092      */
28093     childForms : false,
28094     
28095     /**
28096      * allItems - full list of fields.
28097      * @type {Array}
28098      */
28099     allItems : false,
28100     
28101     /**
28102      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
28103      * element by passing it or its id or mask the form itself by passing in true.
28104      * @type Mixed
28105      */
28106     waitMsgTarget : false,
28107
28108     // private
28109     initEl : function(el){
28110         this.el = Roo.get(el);
28111         this.id = this.el.id || Roo.id();
28112         this.el.on('submit', this.onSubmit, this);
28113         this.el.addClass('x-form');
28114     },
28115
28116     // private
28117     onSubmit : function(e){
28118         e.stopEvent();
28119     },
28120
28121     /**
28122      * Returns true if client-side validation on the form is successful.
28123      * @return Boolean
28124      */
28125     isValid : function(){
28126         var valid = true;
28127         this.items.each(function(f){
28128            if(!f.validate()){
28129                valid = false;
28130            }
28131         });
28132         return valid;
28133     },
28134
28135     /**
28136      * Returns true if any fields in this form have changed since their original load.
28137      * @return Boolean
28138      */
28139     isDirty : function(){
28140         var dirty = false;
28141         this.items.each(function(f){
28142            if(f.isDirty()){
28143                dirty = true;
28144                return false;
28145            }
28146         });
28147         return dirty;
28148     },
28149
28150     /**
28151      * Performs a predefined action (submit or load) or custom actions you define on this form.
28152      * @param {String} actionName The name of the action type
28153      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
28154      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
28155      * accept other config options):
28156      * <pre>
28157 Property          Type             Description
28158 ----------------  ---------------  ----------------------------------------------------------------------------------
28159 url               String           The url for the action (defaults to the form's url)
28160 method            String           The form method to use (defaults to the form's method, or POST if not defined)
28161 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
28162 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
28163                                    validate the form on the client (defaults to false)
28164      * </pre>
28165      * @return {BasicForm} this
28166      */
28167     doAction : function(action, options){
28168         if(typeof action == 'string'){
28169             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
28170         }
28171         if(this.fireEvent('beforeaction', this, action) !== false){
28172             this.beforeAction(action);
28173             action.run.defer(100, action);
28174         }
28175         return this;
28176     },
28177
28178     /**
28179      * Shortcut to do a submit action.
28180      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28181      * @return {BasicForm} this
28182      */
28183     submit : function(options){
28184         this.doAction('submit', options);
28185         return this;
28186     },
28187
28188     /**
28189      * Shortcut to do a load action.
28190      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28191      * @return {BasicForm} this
28192      */
28193     load : function(options){
28194         this.doAction('load', options);
28195         return this;
28196     },
28197
28198     /**
28199      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
28200      * @param {Record} record The record to edit
28201      * @return {BasicForm} this
28202      */
28203     updateRecord : function(record){
28204         record.beginEdit();
28205         var fs = record.fields;
28206         fs.each(function(f){
28207             var field = this.findField(f.name);
28208             if(field){
28209                 record.set(f.name, field.getValue());
28210             }
28211         }, this);
28212         record.endEdit();
28213         return this;
28214     },
28215
28216     /**
28217      * Loads an Roo.data.Record into this form.
28218      * @param {Record} record The record to load
28219      * @return {BasicForm} this
28220      */
28221     loadRecord : function(record){
28222         this.setValues(record.data);
28223         return this;
28224     },
28225
28226     // private
28227     beforeAction : function(action){
28228         var o = action.options;
28229         
28230        
28231         if(this.waitMsgTarget === true){
28232             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
28233         }else if(this.waitMsgTarget){
28234             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
28235             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
28236         }else {
28237             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
28238         }
28239          
28240     },
28241
28242     // private
28243     afterAction : function(action, success){
28244         this.activeAction = null;
28245         var o = action.options;
28246         
28247         if(this.waitMsgTarget === true){
28248             this.el.unmask();
28249         }else if(this.waitMsgTarget){
28250             this.waitMsgTarget.unmask();
28251         }else{
28252             Roo.MessageBox.updateProgress(1);
28253             Roo.MessageBox.hide();
28254         }
28255          
28256         if(success){
28257             if(o.reset){
28258                 this.reset();
28259             }
28260             Roo.callback(o.success, o.scope, [this, action]);
28261             this.fireEvent('actioncomplete', this, action);
28262             
28263         }else{
28264             
28265             // failure condition..
28266             // we have a scenario where updates need confirming.
28267             // eg. if a locking scenario exists..
28268             // we look for { errors : { needs_confirm : true }} in the response.
28269             if (
28270                 (typeof(action.result) != 'undefined')  &&
28271                 (typeof(action.result.errors) != 'undefined')  &&
28272                 (typeof(action.result.errors.needs_confirm) != 'undefined')
28273            ){
28274                 var _t = this;
28275                 Roo.MessageBox.confirm(
28276                     "Change requires confirmation",
28277                     action.result.errorMsg,
28278                     function(r) {
28279                         if (r != 'yes') {
28280                             return;
28281                         }
28282                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
28283                     }
28284                     
28285                 );
28286                 
28287                 
28288                 
28289                 return;
28290             }
28291             
28292             Roo.callback(o.failure, o.scope, [this, action]);
28293             // show an error message if no failed handler is set..
28294             if (!this.hasListener('actionfailed')) {
28295                 Roo.MessageBox.alert("Error",
28296                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
28297                         action.result.errorMsg :
28298                         "Saving Failed, please check your entries or try again"
28299                 );
28300             }
28301             
28302             this.fireEvent('actionfailed', this, action);
28303         }
28304         
28305     },
28306
28307     /**
28308      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
28309      * @param {String} id The value to search for
28310      * @return Field
28311      */
28312     findField : function(id){
28313         var field = this.items.get(id);
28314         if(!field){
28315             this.items.each(function(f){
28316                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
28317                     field = f;
28318                     return false;
28319                 }
28320             });
28321         }
28322         return field || null;
28323     },
28324
28325     /**
28326      * Add a secondary form to this one, 
28327      * Used to provide tabbed forms. One form is primary, with hidden values 
28328      * which mirror the elements from the other forms.
28329      * 
28330      * @param {Roo.form.Form} form to add.
28331      * 
28332      */
28333     addForm : function(form)
28334     {
28335        
28336         if (this.childForms.indexOf(form) > -1) {
28337             // already added..
28338             return;
28339         }
28340         this.childForms.push(form);
28341         var n = '';
28342         Roo.each(form.allItems, function (fe) {
28343             
28344             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
28345             if (this.findField(n)) { // already added..
28346                 return;
28347             }
28348             var add = new Roo.form.Hidden({
28349                 name : n
28350             });
28351             add.render(this.el);
28352             
28353             this.add( add );
28354         }, this);
28355         
28356     },
28357     /**
28358      * Mark fields in this form invalid in bulk.
28359      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
28360      * @return {BasicForm} this
28361      */
28362     markInvalid : function(errors){
28363         if(errors instanceof Array){
28364             for(var i = 0, len = errors.length; i < len; i++){
28365                 var fieldError = errors[i];
28366                 var f = this.findField(fieldError.id);
28367                 if(f){
28368                     f.markInvalid(fieldError.msg);
28369                 }
28370             }
28371         }else{
28372             var field, id;
28373             for(id in errors){
28374                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
28375                     field.markInvalid(errors[id]);
28376                 }
28377             }
28378         }
28379         Roo.each(this.childForms || [], function (f) {
28380             f.markInvalid(errors);
28381         });
28382         
28383         return this;
28384     },
28385
28386     /**
28387      * Set values for fields in this form in bulk.
28388      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
28389      * @return {BasicForm} this
28390      */
28391     setValues : function(values){
28392         if(values instanceof Array){ // array of objects
28393             for(var i = 0, len = values.length; i < len; i++){
28394                 var v = values[i];
28395                 var f = this.findField(v.id);
28396                 if(f){
28397                     f.setValue(v.value);
28398                     if(this.trackResetOnLoad){
28399                         f.originalValue = f.getValue();
28400                     }
28401                 }
28402             }
28403         }else{ // object hash
28404             var field, id;
28405             for(id in values){
28406                 if(typeof values[id] != 'function' && (field = this.findField(id))){
28407                     
28408                     if (field.setFromData && 
28409                         field.valueField && 
28410                         field.displayField &&
28411                         // combos' with local stores can 
28412                         // be queried via setValue()
28413                         // to set their value..
28414                         (field.store && !field.store.isLocal)
28415                         ) {
28416                         // it's a combo
28417                         var sd = { };
28418                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
28419                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
28420                         field.setFromData(sd);
28421                         
28422                     } else {
28423                         field.setValue(values[id]);
28424                     }
28425                     
28426                     
28427                     if(this.trackResetOnLoad){
28428                         field.originalValue = field.getValue();
28429                     }
28430                 }
28431             }
28432         }
28433          
28434         Roo.each(this.childForms || [], function (f) {
28435             f.setValues(values);
28436         });
28437                 
28438         return this;
28439     },
28440
28441     /**
28442      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
28443      * they are returned as an array.
28444      * @param {Boolean} asString
28445      * @return {Object}
28446      */
28447     getValues : function(asString){
28448         if (this.childForms) {
28449             // copy values from the child forms
28450             Roo.each(this.childForms, function (f) {
28451                 this.setValues(f.getValues());
28452             }, this);
28453         }
28454         
28455         
28456         
28457         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
28458         if(asString === true){
28459             return fs;
28460         }
28461         return Roo.urlDecode(fs);
28462     },
28463     
28464     /**
28465      * Returns the fields in this form as an object with key/value pairs. 
28466      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
28467      * @return {Object}
28468      */
28469     getFieldValues : function(with_hidden)
28470     {
28471         if (this.childForms) {
28472             // copy values from the child forms
28473             // should this call getFieldValues - probably not as we do not currently copy
28474             // hidden fields when we generate..
28475             Roo.each(this.childForms, function (f) {
28476                 this.setValues(f.getValues());
28477             }, this);
28478         }
28479         
28480         var ret = {};
28481         this.items.each(function(f){
28482             if (!f.getName()) {
28483                 return;
28484             }
28485             var v = f.getValue();
28486             if (f.inputType =='radio') {
28487                 if (typeof(ret[f.getName()]) == 'undefined') {
28488                     ret[f.getName()] = ''; // empty..
28489                 }
28490                 
28491                 if (!f.el.dom.checked) {
28492                     return;
28493                     
28494                 }
28495                 v = f.el.dom.value;
28496                 
28497             }
28498             
28499             // not sure if this supported any more..
28500             if ((typeof(v) == 'object') && f.getRawValue) {
28501                 v = f.getRawValue() ; // dates..
28502             }
28503             // combo boxes where name != hiddenName...
28504             if (f.name != f.getName()) {
28505                 ret[f.name] = f.getRawValue();
28506             }
28507             ret[f.getName()] = v;
28508         });
28509         
28510         return ret;
28511     },
28512
28513     /**
28514      * Clears all invalid messages in this form.
28515      * @return {BasicForm} this
28516      */
28517     clearInvalid : function(){
28518         this.items.each(function(f){
28519            f.clearInvalid();
28520         });
28521         
28522         Roo.each(this.childForms || [], function (f) {
28523             f.clearInvalid();
28524         });
28525         
28526         
28527         return this;
28528     },
28529
28530     /**
28531      * Resets this form.
28532      * @return {BasicForm} this
28533      */
28534     reset : function(){
28535         this.items.each(function(f){
28536             f.reset();
28537         });
28538         
28539         Roo.each(this.childForms || [], function (f) {
28540             f.reset();
28541         });
28542        
28543         
28544         return this;
28545     },
28546
28547     /**
28548      * Add Roo.form components to this form.
28549      * @param {Field} field1
28550      * @param {Field} field2 (optional)
28551      * @param {Field} etc (optional)
28552      * @return {BasicForm} this
28553      */
28554     add : function(){
28555         this.items.addAll(Array.prototype.slice.call(arguments, 0));
28556         return this;
28557     },
28558
28559
28560     /**
28561      * Removes a field from the items collection (does NOT remove its markup).
28562      * @param {Field} field
28563      * @return {BasicForm} this
28564      */
28565     remove : function(field){
28566         this.items.remove(field);
28567         return this;
28568     },
28569
28570     /**
28571      * Looks at the fields in this form, checks them for an id attribute,
28572      * and calls applyTo on the existing dom element with that id.
28573      * @return {BasicForm} this
28574      */
28575     render : function(){
28576         this.items.each(function(f){
28577             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
28578                 f.applyTo(f.id);
28579             }
28580         });
28581         return this;
28582     },
28583
28584     /**
28585      * Calls {@link Ext#apply} for all fields in this form with the passed object.
28586      * @param {Object} values
28587      * @return {BasicForm} this
28588      */
28589     applyToFields : function(o){
28590         this.items.each(function(f){
28591            Roo.apply(f, o);
28592         });
28593         return this;
28594     },
28595
28596     /**
28597      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
28598      * @param {Object} values
28599      * @return {BasicForm} this
28600      */
28601     applyIfToFields : function(o){
28602         this.items.each(function(f){
28603            Roo.applyIf(f, o);
28604         });
28605         return this;
28606     }
28607 });
28608
28609 // back compat
28610 Roo.BasicForm = Roo.form.BasicForm;/*
28611  * Based on:
28612  * Ext JS Library 1.1.1
28613  * Copyright(c) 2006-2007, Ext JS, LLC.
28614  *
28615  * Originally Released Under LGPL - original licence link has changed is not relivant.
28616  *
28617  * Fork - LGPL
28618  * <script type="text/javascript">
28619  */
28620
28621 /**
28622  * @class Roo.form.Form
28623  * @extends Roo.form.BasicForm
28624  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
28625  * @constructor
28626  * @param {Object} config Configuration options
28627  */
28628 Roo.form.Form = function(config){
28629     var xitems =  [];
28630     if (config.items) {
28631         xitems = config.items;
28632         delete config.items;
28633     }
28634    
28635     
28636     Roo.form.Form.superclass.constructor.call(this, null, config);
28637     this.url = this.url || this.action;
28638     if(!this.root){
28639         this.root = new Roo.form.Layout(Roo.applyIf({
28640             id: Roo.id()
28641         }, config));
28642     }
28643     this.active = this.root;
28644     /**
28645      * Array of all the buttons that have been added to this form via {@link addButton}
28646      * @type Array
28647      */
28648     this.buttons = [];
28649     this.allItems = [];
28650     this.addEvents({
28651         /**
28652          * @event clientvalidation
28653          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
28654          * @param {Form} this
28655          * @param {Boolean} valid true if the form has passed client-side validation
28656          */
28657         clientvalidation: true,
28658         /**
28659          * @event rendered
28660          * Fires when the form is rendered
28661          * @param {Roo.form.Form} form
28662          */
28663         rendered : true
28664     });
28665     
28666     if (this.progressUrl) {
28667             // push a hidden field onto the list of fields..
28668             this.addxtype( {
28669                     xns: Roo.form, 
28670                     xtype : 'Hidden', 
28671                     name : 'UPLOAD_IDENTIFIER' 
28672             });
28673         }
28674         
28675     
28676     Roo.each(xitems, this.addxtype, this);
28677     
28678     
28679     
28680 };
28681
28682 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
28683     /**
28684      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
28685      */
28686     /**
28687      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
28688      */
28689     /**
28690      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
28691      */
28692     buttonAlign:'center',
28693
28694     /**
28695      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
28696      */
28697     minButtonWidth:75,
28698
28699     /**
28700      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
28701      * This property cascades to child containers if not set.
28702      */
28703     labelAlign:'left',
28704
28705     /**
28706      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
28707      * fires a looping event with that state. This is required to bind buttons to the valid
28708      * state using the config value formBind:true on the button.
28709      */
28710     monitorValid : false,
28711
28712     /**
28713      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
28714      */
28715     monitorPoll : 200,
28716     
28717     /**
28718      * @cfg {String} progressUrl - Url to return progress data 
28719      */
28720     
28721     progressUrl : false,
28722   
28723     /**
28724      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
28725      * fields are added and the column is closed. If no fields are passed the column remains open
28726      * until end() is called.
28727      * @param {Object} config The config to pass to the column
28728      * @param {Field} field1 (optional)
28729      * @param {Field} field2 (optional)
28730      * @param {Field} etc (optional)
28731      * @return Column The column container object
28732      */
28733     column : function(c){
28734         var col = new Roo.form.Column(c);
28735         this.start(col);
28736         if(arguments.length > 1){ // duplicate code required because of Opera
28737             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28738             this.end();
28739         }
28740         return col;
28741     },
28742
28743     /**
28744      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
28745      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
28746      * until end() is called.
28747      * @param {Object} config The config to pass to the fieldset
28748      * @param {Field} field1 (optional)
28749      * @param {Field} field2 (optional)
28750      * @param {Field} etc (optional)
28751      * @return FieldSet The fieldset container object
28752      */
28753     fieldset : function(c){
28754         var fs = new Roo.form.FieldSet(c);
28755         this.start(fs);
28756         if(arguments.length > 1){ // duplicate code required because of Opera
28757             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28758             this.end();
28759         }
28760         return fs;
28761     },
28762
28763     /**
28764      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
28765      * fields are added and the container is closed. If no fields are passed the container remains open
28766      * until end() is called.
28767      * @param {Object} config The config to pass to the Layout
28768      * @param {Field} field1 (optional)
28769      * @param {Field} field2 (optional)
28770      * @param {Field} etc (optional)
28771      * @return Layout The container object
28772      */
28773     container : function(c){
28774         var l = new Roo.form.Layout(c);
28775         this.start(l);
28776         if(arguments.length > 1){ // duplicate code required because of Opera
28777             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28778             this.end();
28779         }
28780         return l;
28781     },
28782
28783     /**
28784      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
28785      * @param {Object} container A Roo.form.Layout or subclass of Layout
28786      * @return {Form} this
28787      */
28788     start : function(c){
28789         // cascade label info
28790         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
28791         this.active.stack.push(c);
28792         c.ownerCt = this.active;
28793         this.active = c;
28794         return this;
28795     },
28796
28797     /**
28798      * Closes the current open container
28799      * @return {Form} this
28800      */
28801     end : function(){
28802         if(this.active == this.root){
28803             return this;
28804         }
28805         this.active = this.active.ownerCt;
28806         return this;
28807     },
28808
28809     /**
28810      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
28811      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
28812      * as the label of the field.
28813      * @param {Field} field1
28814      * @param {Field} field2 (optional)
28815      * @param {Field} etc. (optional)
28816      * @return {Form} this
28817      */
28818     add : function(){
28819         this.active.stack.push.apply(this.active.stack, arguments);
28820         this.allItems.push.apply(this.allItems,arguments);
28821         var r = [];
28822         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
28823             if(a[i].isFormField){
28824                 r.push(a[i]);
28825             }
28826         }
28827         if(r.length > 0){
28828             Roo.form.Form.superclass.add.apply(this, r);
28829         }
28830         return this;
28831     },
28832     
28833
28834     
28835     
28836     
28837      /**
28838      * Find any element that has been added to a form, using it's ID or name
28839      * This can include framesets, columns etc. along with regular fields..
28840      * @param {String} id - id or name to find.
28841      
28842      * @return {Element} e - or false if nothing found.
28843      */
28844     findbyId : function(id)
28845     {
28846         var ret = false;
28847         if (!id) {
28848             return ret;
28849         }
28850         Roo.each(this.allItems, function(f){
28851             if (f.id == id || f.name == id ){
28852                 ret = f;
28853                 return false;
28854             }
28855         });
28856         return ret;
28857     },
28858
28859     
28860     
28861     /**
28862      * Render this form into the passed container. This should only be called once!
28863      * @param {String/HTMLElement/Element} container The element this component should be rendered into
28864      * @return {Form} this
28865      */
28866     render : function(ct)
28867     {
28868         
28869         
28870         
28871         ct = Roo.get(ct);
28872         var o = this.autoCreate || {
28873             tag: 'form',
28874             method : this.method || 'POST',
28875             id : this.id || Roo.id()
28876         };
28877         this.initEl(ct.createChild(o));
28878
28879         this.root.render(this.el);
28880         
28881        
28882              
28883         this.items.each(function(f){
28884             f.render('x-form-el-'+f.id);
28885         });
28886
28887         if(this.buttons.length > 0){
28888             // tables are required to maintain order and for correct IE layout
28889             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
28890                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
28891                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
28892             }}, null, true);
28893             var tr = tb.getElementsByTagName('tr')[0];
28894             for(var i = 0, len = this.buttons.length; i < len; i++) {
28895                 var b = this.buttons[i];
28896                 var td = document.createElement('td');
28897                 td.className = 'x-form-btn-td';
28898                 b.render(tr.appendChild(td));
28899             }
28900         }
28901         if(this.monitorValid){ // initialize after render
28902             this.startMonitoring();
28903         }
28904         this.fireEvent('rendered', this);
28905         return this;
28906     },
28907
28908     /**
28909      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
28910      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
28911      * object or a valid Roo.DomHelper element config
28912      * @param {Function} handler The function called when the button is clicked
28913      * @param {Object} scope (optional) The scope of the handler function
28914      * @return {Roo.Button}
28915      */
28916     addButton : function(config, handler, scope){
28917         var bc = {
28918             handler: handler,
28919             scope: scope,
28920             minWidth: this.minButtonWidth,
28921             hideParent:true
28922         };
28923         if(typeof config == "string"){
28924             bc.text = config;
28925         }else{
28926             Roo.apply(bc, config);
28927         }
28928         var btn = new Roo.Button(null, bc);
28929         this.buttons.push(btn);
28930         return btn;
28931     },
28932
28933      /**
28934      * Adds a series of form elements (using the xtype property as the factory method.
28935      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
28936      * @param {Object} config 
28937      */
28938     
28939     addxtype : function()
28940     {
28941         var ar = Array.prototype.slice.call(arguments, 0);
28942         var ret = false;
28943         for(var i = 0; i < ar.length; i++) {
28944             if (!ar[i]) {
28945                 continue; // skip -- if this happends something invalid got sent, we 
28946                 // should ignore it, as basically that interface element will not show up
28947                 // and that should be pretty obvious!!
28948             }
28949             
28950             if (Roo.form[ar[i].xtype]) {
28951                 ar[i].form = this;
28952                 var fe = Roo.factory(ar[i], Roo.form);
28953                 if (!ret) {
28954                     ret = fe;
28955                 }
28956                 fe.form = this;
28957                 if (fe.store) {
28958                     fe.store.form = this;
28959                 }
28960                 if (fe.isLayout) {  
28961                          
28962                     this.start(fe);
28963                     this.allItems.push(fe);
28964                     if (fe.items && fe.addxtype) {
28965                         fe.addxtype.apply(fe, fe.items);
28966                         delete fe.items;
28967                     }
28968                      this.end();
28969                     continue;
28970                 }
28971                 
28972                 
28973                  
28974                 this.add(fe);
28975               //  console.log('adding ' + ar[i].xtype);
28976             }
28977             if (ar[i].xtype == 'Button') {  
28978                 //console.log('adding button');
28979                 //console.log(ar[i]);
28980                 this.addButton(ar[i]);
28981                 this.allItems.push(fe);
28982                 continue;
28983             }
28984             
28985             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
28986                 alert('end is not supported on xtype any more, use items');
28987             //    this.end();
28988             //    //console.log('adding end');
28989             }
28990             
28991         }
28992         return ret;
28993     },
28994     
28995     /**
28996      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
28997      * option "monitorValid"
28998      */
28999     startMonitoring : function(){
29000         if(!this.bound){
29001             this.bound = true;
29002             Roo.TaskMgr.start({
29003                 run : this.bindHandler,
29004                 interval : this.monitorPoll || 200,
29005                 scope: this
29006             });
29007         }
29008     },
29009
29010     /**
29011      * Stops monitoring of the valid state of this form
29012      */
29013     stopMonitoring : function(){
29014         this.bound = false;
29015     },
29016
29017     // private
29018     bindHandler : function(){
29019         if(!this.bound){
29020             return false; // stops binding
29021         }
29022         var valid = true;
29023         this.items.each(function(f){
29024             if(!f.isValid(true)){
29025                 valid = false;
29026                 return false;
29027             }
29028         });
29029         for(var i = 0, len = this.buttons.length; i < len; i++){
29030             var btn = this.buttons[i];
29031             if(btn.formBind === true && btn.disabled === valid){
29032                 btn.setDisabled(!valid);
29033             }
29034         }
29035         this.fireEvent('clientvalidation', this, valid);
29036     }
29037     
29038     
29039     
29040     
29041     
29042     
29043     
29044     
29045 });
29046
29047
29048 // back compat
29049 Roo.Form = Roo.form.Form;
29050 /*
29051  * Based on:
29052  * Ext JS Library 1.1.1
29053  * Copyright(c) 2006-2007, Ext JS, LLC.
29054  *
29055  * Originally Released Under LGPL - original licence link has changed is not relivant.
29056  *
29057  * Fork - LGPL
29058  * <script type="text/javascript">
29059  */
29060  
29061  /**
29062  * @class Roo.form.Action
29063  * Internal Class used to handle form actions
29064  * @constructor
29065  * @param {Roo.form.BasicForm} el The form element or its id
29066  * @param {Object} config Configuration options
29067  */
29068  
29069  
29070 // define the action interface
29071 Roo.form.Action = function(form, options){
29072     this.form = form;
29073     this.options = options || {};
29074 };
29075 /**
29076  * Client Validation Failed
29077  * @const 
29078  */
29079 Roo.form.Action.CLIENT_INVALID = 'client';
29080 /**
29081  * Server Validation Failed
29082  * @const 
29083  */
29084  Roo.form.Action.SERVER_INVALID = 'server';
29085  /**
29086  * Connect to Server Failed
29087  * @const 
29088  */
29089 Roo.form.Action.CONNECT_FAILURE = 'connect';
29090 /**
29091  * Reading Data from Server Failed
29092  * @const 
29093  */
29094 Roo.form.Action.LOAD_FAILURE = 'load';
29095
29096 Roo.form.Action.prototype = {
29097     type : 'default',
29098     failureType : undefined,
29099     response : undefined,
29100     result : undefined,
29101
29102     // interface method
29103     run : function(options){
29104
29105     },
29106
29107     // interface method
29108     success : function(response){
29109
29110     },
29111
29112     // interface method
29113     handleResponse : function(response){
29114
29115     },
29116
29117     // default connection failure
29118     failure : function(response){
29119         
29120         this.response = response;
29121         this.failureType = Roo.form.Action.CONNECT_FAILURE;
29122         this.form.afterAction(this, false);
29123     },
29124
29125     processResponse : function(response){
29126         this.response = response;
29127         if(!response.responseText){
29128             return true;
29129         }
29130         this.result = this.handleResponse(response);
29131         return this.result;
29132     },
29133
29134     // utility functions used internally
29135     getUrl : function(appendParams){
29136         var url = this.options.url || this.form.url || this.form.el.dom.action;
29137         if(appendParams){
29138             var p = this.getParams();
29139             if(p){
29140                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
29141             }
29142         }
29143         return url;
29144     },
29145
29146     getMethod : function(){
29147         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
29148     },
29149
29150     getParams : function(){
29151         var bp = this.form.baseParams;
29152         var p = this.options.params;
29153         if(p){
29154             if(typeof p == "object"){
29155                 p = Roo.urlEncode(Roo.applyIf(p, bp));
29156             }else if(typeof p == 'string' && bp){
29157                 p += '&' + Roo.urlEncode(bp);
29158             }
29159         }else if(bp){
29160             p = Roo.urlEncode(bp);
29161         }
29162         return p;
29163     },
29164
29165     createCallback : function(){
29166         return {
29167             success: this.success,
29168             failure: this.failure,
29169             scope: this,
29170             timeout: (this.form.timeout*1000),
29171             upload: this.form.fileUpload ? this.success : undefined
29172         };
29173     }
29174 };
29175
29176 Roo.form.Action.Submit = function(form, options){
29177     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
29178 };
29179
29180 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
29181     type : 'submit',
29182
29183     haveProgress : false,
29184     uploadComplete : false,
29185     
29186     // uploadProgress indicator.
29187     uploadProgress : function()
29188     {
29189         if (!this.form.progressUrl) {
29190             return;
29191         }
29192         
29193         if (!this.haveProgress) {
29194             Roo.MessageBox.progress("Uploading", "Uploading");
29195         }
29196         if (this.uploadComplete) {
29197            Roo.MessageBox.hide();
29198            return;
29199         }
29200         
29201         this.haveProgress = true;
29202    
29203         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
29204         
29205         var c = new Roo.data.Connection();
29206         c.request({
29207             url : this.form.progressUrl,
29208             params: {
29209                 id : uid
29210             },
29211             method: 'GET',
29212             success : function(req){
29213                //console.log(data);
29214                 var rdata = false;
29215                 var edata;
29216                 try  {
29217                    rdata = Roo.decode(req.responseText)
29218                 } catch (e) {
29219                     Roo.log("Invalid data from server..");
29220                     Roo.log(edata);
29221                     return;
29222                 }
29223                 if (!rdata || !rdata.success) {
29224                     Roo.log(rdata);
29225                     Roo.MessageBox.alert(Roo.encode(rdata));
29226                     return;
29227                 }
29228                 var data = rdata.data;
29229                 
29230                 if (this.uploadComplete) {
29231                    Roo.MessageBox.hide();
29232                    return;
29233                 }
29234                    
29235                 if (data){
29236                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
29237                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
29238                     );
29239                 }
29240                 this.uploadProgress.defer(2000,this);
29241             },
29242        
29243             failure: function(data) {
29244                 Roo.log('progress url failed ');
29245                 Roo.log(data);
29246             },
29247             scope : this
29248         });
29249            
29250     },
29251     
29252     
29253     run : function()
29254     {
29255         // run get Values on the form, so it syncs any secondary forms.
29256         this.form.getValues();
29257         
29258         var o = this.options;
29259         var method = this.getMethod();
29260         var isPost = method == 'POST';
29261         if(o.clientValidation === false || this.form.isValid()){
29262             
29263             if (this.form.progressUrl) {
29264                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
29265                     (new Date() * 1) + '' + Math.random());
29266                     
29267             } 
29268             
29269             
29270             Roo.Ajax.request(Roo.apply(this.createCallback(), {
29271                 form:this.form.el.dom,
29272                 url:this.getUrl(!isPost),
29273                 method: method,
29274                 params:isPost ? this.getParams() : null,
29275                 isUpload: this.form.fileUpload
29276             }));
29277             
29278             this.uploadProgress();
29279
29280         }else if (o.clientValidation !== false){ // client validation failed
29281             this.failureType = Roo.form.Action.CLIENT_INVALID;
29282             this.form.afterAction(this, false);
29283         }
29284     },
29285
29286     success : function(response)
29287     {
29288         this.uploadComplete= true;
29289         if (this.haveProgress) {
29290             Roo.MessageBox.hide();
29291         }
29292         
29293         
29294         var result = this.processResponse(response);
29295         if(result === true || result.success){
29296             this.form.afterAction(this, true);
29297             return;
29298         }
29299         if(result.errors){
29300             this.form.markInvalid(result.errors);
29301             this.failureType = Roo.form.Action.SERVER_INVALID;
29302         }
29303         this.form.afterAction(this, false);
29304     },
29305     failure : function(response)
29306     {
29307         this.uploadComplete= true;
29308         if (this.haveProgress) {
29309             Roo.MessageBox.hide();
29310         }
29311         
29312         this.response = response;
29313         this.failureType = Roo.form.Action.CONNECT_FAILURE;
29314         this.form.afterAction(this, false);
29315     },
29316     
29317     handleResponse : function(response){
29318         if(this.form.errorReader){
29319             var rs = this.form.errorReader.read(response);
29320             var errors = [];
29321             if(rs.records){
29322                 for(var i = 0, len = rs.records.length; i < len; i++) {
29323                     var r = rs.records[i];
29324                     errors[i] = r.data;
29325                 }
29326             }
29327             if(errors.length < 1){
29328                 errors = null;
29329             }
29330             return {
29331                 success : rs.success,
29332                 errors : errors
29333             };
29334         }
29335         var ret = false;
29336         try {
29337             ret = Roo.decode(response.responseText);
29338         } catch (e) {
29339             ret = {
29340                 success: false,
29341                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
29342                 errors : []
29343             };
29344         }
29345         return ret;
29346         
29347     }
29348 });
29349
29350
29351 Roo.form.Action.Load = function(form, options){
29352     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
29353     this.reader = this.form.reader;
29354 };
29355
29356 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
29357     type : 'load',
29358
29359     run : function(){
29360         
29361         Roo.Ajax.request(Roo.apply(
29362                 this.createCallback(), {
29363                     method:this.getMethod(),
29364                     url:this.getUrl(false),
29365                     params:this.getParams()
29366         }));
29367     },
29368
29369     success : function(response){
29370         
29371         var result = this.processResponse(response);
29372         if(result === true || !result.success || !result.data){
29373             this.failureType = Roo.form.Action.LOAD_FAILURE;
29374             this.form.afterAction(this, false);
29375             return;
29376         }
29377         this.form.clearInvalid();
29378         this.form.setValues(result.data);
29379         this.form.afterAction(this, true);
29380     },
29381
29382     handleResponse : function(response){
29383         if(this.form.reader){
29384             var rs = this.form.reader.read(response);
29385             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
29386             return {
29387                 success : rs.success,
29388                 data : data
29389             };
29390         }
29391         return Roo.decode(response.responseText);
29392     }
29393 });
29394
29395 Roo.form.Action.ACTION_TYPES = {
29396     'load' : Roo.form.Action.Load,
29397     'submit' : Roo.form.Action.Submit
29398 };/*
29399  * Based on:
29400  * Ext JS Library 1.1.1
29401  * Copyright(c) 2006-2007, Ext JS, LLC.
29402  *
29403  * Originally Released Under LGPL - original licence link has changed is not relivant.
29404  *
29405  * Fork - LGPL
29406  * <script type="text/javascript">
29407  */
29408  
29409 /**
29410  * @class Roo.form.Layout
29411  * @extends Roo.Component
29412  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
29413  * @constructor
29414  * @param {Object} config Configuration options
29415  */
29416 Roo.form.Layout = function(config){
29417     var xitems = [];
29418     if (config.items) {
29419         xitems = config.items;
29420         delete config.items;
29421     }
29422     Roo.form.Layout.superclass.constructor.call(this, config);
29423     this.stack = [];
29424     Roo.each(xitems, this.addxtype, this);
29425      
29426 };
29427
29428 Roo.extend(Roo.form.Layout, Roo.Component, {
29429     /**
29430      * @cfg {String/Object} autoCreate
29431      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
29432      */
29433     /**
29434      * @cfg {String/Object/Function} style
29435      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
29436      * a function which returns such a specification.
29437      */
29438     /**
29439      * @cfg {String} labelAlign
29440      * Valid values are "left," "top" and "right" (defaults to "left")
29441      */
29442     /**
29443      * @cfg {Number} labelWidth
29444      * Fixed width in pixels of all field labels (defaults to undefined)
29445      */
29446     /**
29447      * @cfg {Boolean} clear
29448      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
29449      */
29450     clear : true,
29451     /**
29452      * @cfg {String} labelSeparator
29453      * The separator to use after field labels (defaults to ':')
29454      */
29455     labelSeparator : ':',
29456     /**
29457      * @cfg {Boolean} hideLabels
29458      * True to suppress the display of field labels in this layout (defaults to false)
29459      */
29460     hideLabels : false,
29461
29462     // private
29463     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
29464     
29465     isLayout : true,
29466     
29467     // private
29468     onRender : function(ct, position){
29469         if(this.el){ // from markup
29470             this.el = Roo.get(this.el);
29471         }else {  // generate
29472             var cfg = this.getAutoCreate();
29473             this.el = ct.createChild(cfg, position);
29474         }
29475         if(this.style){
29476             this.el.applyStyles(this.style);
29477         }
29478         if(this.labelAlign){
29479             this.el.addClass('x-form-label-'+this.labelAlign);
29480         }
29481         if(this.hideLabels){
29482             this.labelStyle = "display:none";
29483             this.elementStyle = "padding-left:0;";
29484         }else{
29485             if(typeof this.labelWidth == 'number'){
29486                 this.labelStyle = "width:"+this.labelWidth+"px;";
29487                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
29488             }
29489             if(this.labelAlign == 'top'){
29490                 this.labelStyle = "width:auto;";
29491                 this.elementStyle = "padding-left:0;";
29492             }
29493         }
29494         var stack = this.stack;
29495         var slen = stack.length;
29496         if(slen > 0){
29497             if(!this.fieldTpl){
29498                 var t = new Roo.Template(
29499                     '<div class="x-form-item {5}">',
29500                         '<label for="{0}" style="{2}">{1}{4}</label>',
29501                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
29502                         '</div>',
29503                     '</div><div class="x-form-clear-left"></div>'
29504                 );
29505                 t.disableFormats = true;
29506                 t.compile();
29507                 Roo.form.Layout.prototype.fieldTpl = t;
29508             }
29509             for(var i = 0; i < slen; i++) {
29510                 if(stack[i].isFormField){
29511                     this.renderField(stack[i]);
29512                 }else{
29513                     this.renderComponent(stack[i]);
29514                 }
29515             }
29516         }
29517         if(this.clear){
29518             this.el.createChild({cls:'x-form-clear'});
29519         }
29520     },
29521
29522     // private
29523     renderField : function(f){
29524         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
29525                f.id, //0
29526                f.fieldLabel, //1
29527                f.labelStyle||this.labelStyle||'', //2
29528                this.elementStyle||'', //3
29529                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
29530                f.itemCls||this.itemCls||''  //5
29531        ], true).getPrevSibling());
29532     },
29533
29534     // private
29535     renderComponent : function(c){
29536         c.render(c.isLayout ? this.el : this.el.createChild());    
29537     },
29538     /**
29539      * Adds a object form elements (using the xtype property as the factory method.)
29540      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
29541      * @param {Object} config 
29542      */
29543     addxtype : function(o)
29544     {
29545         // create the lement.
29546         o.form = this.form;
29547         var fe = Roo.factory(o, Roo.form);
29548         this.form.allItems.push(fe);
29549         this.stack.push(fe);
29550         
29551         if (fe.isFormField) {
29552             this.form.items.add(fe);
29553         }
29554          
29555         return fe;
29556     }
29557 });
29558
29559 /**
29560  * @class Roo.form.Column
29561  * @extends Roo.form.Layout
29562  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
29563  * @constructor
29564  * @param {Object} config Configuration options
29565  */
29566 Roo.form.Column = function(config){
29567     Roo.form.Column.superclass.constructor.call(this, config);
29568 };
29569
29570 Roo.extend(Roo.form.Column, Roo.form.Layout, {
29571     /**
29572      * @cfg {Number/String} width
29573      * The fixed width of the column in pixels or CSS value (defaults to "auto")
29574      */
29575     /**
29576      * @cfg {String/Object} autoCreate
29577      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
29578      */
29579
29580     // private
29581     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
29582
29583     // private
29584     onRender : function(ct, position){
29585         Roo.form.Column.superclass.onRender.call(this, ct, position);
29586         if(this.width){
29587             this.el.setWidth(this.width);
29588         }
29589     }
29590 });
29591
29592
29593 /**
29594  * @class Roo.form.Row
29595  * @extends Roo.form.Layout
29596  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
29597  * @constructor
29598  * @param {Object} config Configuration options
29599  */
29600
29601  
29602 Roo.form.Row = function(config){
29603     Roo.form.Row.superclass.constructor.call(this, config);
29604 };
29605  
29606 Roo.extend(Roo.form.Row, Roo.form.Layout, {
29607       /**
29608      * @cfg {Number/String} width
29609      * The fixed width of the column in pixels or CSS value (defaults to "auto")
29610      */
29611     /**
29612      * @cfg {Number/String} height
29613      * The fixed height of the column in pixels or CSS value (defaults to "auto")
29614      */
29615     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
29616     
29617     padWidth : 20,
29618     // private
29619     onRender : function(ct, position){
29620         //console.log('row render');
29621         if(!this.rowTpl){
29622             var t = new Roo.Template(
29623                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
29624                     '<label for="{0}" style="{2}">{1}{4}</label>',
29625                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
29626                     '</div>',
29627                 '</div>'
29628             );
29629             t.disableFormats = true;
29630             t.compile();
29631             Roo.form.Layout.prototype.rowTpl = t;
29632         }
29633         this.fieldTpl = this.rowTpl;
29634         
29635         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
29636         var labelWidth = 100;
29637         
29638         if ((this.labelAlign != 'top')) {
29639             if (typeof this.labelWidth == 'number') {
29640                 labelWidth = this.labelWidth
29641             }
29642             this.padWidth =  20 + labelWidth;
29643             
29644         }
29645         
29646         Roo.form.Column.superclass.onRender.call(this, ct, position);
29647         if(this.width){
29648             this.el.setWidth(this.width);
29649         }
29650         if(this.height){
29651             this.el.setHeight(this.height);
29652         }
29653     },
29654     
29655     // private
29656     renderField : function(f){
29657         f.fieldEl = this.fieldTpl.append(this.el, [
29658                f.id, f.fieldLabel,
29659                f.labelStyle||this.labelStyle||'',
29660                this.elementStyle||'',
29661                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
29662                f.itemCls||this.itemCls||'',
29663                f.width ? f.width + this.padWidth : 160 + this.padWidth
29664        ],true);
29665     }
29666 });
29667  
29668
29669 /**
29670  * @class Roo.form.FieldSet
29671  * @extends Roo.form.Layout
29672  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
29673  * @constructor
29674  * @param {Object} config Configuration options
29675  */
29676 Roo.form.FieldSet = function(config){
29677     Roo.form.FieldSet.superclass.constructor.call(this, config);
29678 };
29679
29680 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
29681     /**
29682      * @cfg {String} legend
29683      * The text to display as the legend for the FieldSet (defaults to '')
29684      */
29685     /**
29686      * @cfg {String/Object} autoCreate
29687      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
29688      */
29689
29690     // private
29691     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
29692
29693     // private
29694     onRender : function(ct, position){
29695         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
29696         if(this.legend){
29697             this.setLegend(this.legend);
29698         }
29699     },
29700
29701     // private
29702     setLegend : function(text){
29703         if(this.rendered){
29704             this.el.child('legend').update(text);
29705         }
29706     }
29707 });/*
29708  * Based on:
29709  * Ext JS Library 1.1.1
29710  * Copyright(c) 2006-2007, Ext JS, LLC.
29711  *
29712  * Originally Released Under LGPL - original licence link has changed is not relivant.
29713  *
29714  * Fork - LGPL
29715  * <script type="text/javascript">
29716  */
29717 /**
29718  * @class Roo.form.VTypes
29719  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
29720  * @singleton
29721  */
29722 Roo.form.VTypes = function(){
29723     // closure these in so they are only created once.
29724     var alpha = /^[a-zA-Z_]+$/;
29725     var alphanum = /^[a-zA-Z0-9_]+$/;
29726     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
29727     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
29728
29729     // All these messages and functions are configurable
29730     return {
29731         /**
29732          * The function used to validate email addresses
29733          * @param {String} value The email address
29734          */
29735         'email' : function(v){
29736             return email.test(v);
29737         },
29738         /**
29739          * The error text to display when the email validation function returns false
29740          * @type String
29741          */
29742         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
29743         /**
29744          * The keystroke filter mask to be applied on email input
29745          * @type RegExp
29746          */
29747         'emailMask' : /[a-z0-9_\.\-@]/i,
29748
29749         /**
29750          * The function used to validate URLs
29751          * @param {String} value The URL
29752          */
29753         'url' : function(v){
29754             return url.test(v);
29755         },
29756         /**
29757          * The error text to display when the url validation function returns false
29758          * @type String
29759          */
29760         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
29761         
29762         /**
29763          * The function used to validate alpha values
29764          * @param {String} value The value
29765          */
29766         'alpha' : function(v){
29767             return alpha.test(v);
29768         },
29769         /**
29770          * The error text to display when the alpha validation function returns false
29771          * @type String
29772          */
29773         'alphaText' : 'This field should only contain letters and _',
29774         /**
29775          * The keystroke filter mask to be applied on alpha input
29776          * @type RegExp
29777          */
29778         'alphaMask' : /[a-z_]/i,
29779
29780         /**
29781          * The function used to validate alphanumeric values
29782          * @param {String} value The value
29783          */
29784         'alphanum' : function(v){
29785             return alphanum.test(v);
29786         },
29787         /**
29788          * The error text to display when the alphanumeric validation function returns false
29789          * @type String
29790          */
29791         'alphanumText' : 'This field should only contain letters, numbers and _',
29792         /**
29793          * The keystroke filter mask to be applied on alphanumeric input
29794          * @type RegExp
29795          */
29796         'alphanumMask' : /[a-z0-9_]/i
29797     };
29798 }();//<script type="text/javascript">
29799
29800 /**
29801  * @class Roo.form.FCKeditor
29802  * @extends Roo.form.TextArea
29803  * Wrapper around the FCKEditor http://www.fckeditor.net
29804  * @constructor
29805  * Creates a new FCKeditor
29806  * @param {Object} config Configuration options
29807  */
29808 Roo.form.FCKeditor = function(config){
29809     Roo.form.FCKeditor.superclass.constructor.call(this, config);
29810     this.addEvents({
29811          /**
29812          * @event editorinit
29813          * Fired when the editor is initialized - you can add extra handlers here..
29814          * @param {FCKeditor} this
29815          * @param {Object} the FCK object.
29816          */
29817         editorinit : true
29818     });
29819     
29820     
29821 };
29822 Roo.form.FCKeditor.editors = { };
29823 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
29824 {
29825     //defaultAutoCreate : {
29826     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
29827     //},
29828     // private
29829     /**
29830      * @cfg {Object} fck options - see fck manual for details.
29831      */
29832     fckconfig : false,
29833     
29834     /**
29835      * @cfg {Object} fck toolbar set (Basic or Default)
29836      */
29837     toolbarSet : 'Basic',
29838     /**
29839      * @cfg {Object} fck BasePath
29840      */ 
29841     basePath : '/fckeditor/',
29842     
29843     
29844     frame : false,
29845     
29846     value : '',
29847     
29848    
29849     onRender : function(ct, position)
29850     {
29851         if(!this.el){
29852             this.defaultAutoCreate = {
29853                 tag: "textarea",
29854                 style:"width:300px;height:60px;",
29855                 autocomplete: "off"
29856             };
29857         }
29858         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
29859         /*
29860         if(this.grow){
29861             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
29862             if(this.preventScrollbars){
29863                 this.el.setStyle("overflow", "hidden");
29864             }
29865             this.el.setHeight(this.growMin);
29866         }
29867         */
29868         //console.log('onrender' + this.getId() );
29869         Roo.form.FCKeditor.editors[this.getId()] = this;
29870          
29871
29872         this.replaceTextarea() ;
29873         
29874     },
29875     
29876     getEditor : function() {
29877         return this.fckEditor;
29878     },
29879     /**
29880      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
29881      * @param {Mixed} value The value to set
29882      */
29883     
29884     
29885     setValue : function(value)
29886     {
29887         //console.log('setValue: ' + value);
29888         
29889         if(typeof(value) == 'undefined') { // not sure why this is happending...
29890             return;
29891         }
29892         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
29893         
29894         //if(!this.el || !this.getEditor()) {
29895         //    this.value = value;
29896             //this.setValue.defer(100,this,[value]);    
29897         //    return;
29898         //} 
29899         
29900         if(!this.getEditor()) {
29901             return;
29902         }
29903         
29904         this.getEditor().SetData(value);
29905         
29906         //
29907
29908     },
29909
29910     /**
29911      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
29912      * @return {Mixed} value The field value
29913      */
29914     getValue : function()
29915     {
29916         
29917         if (this.frame && this.frame.dom.style.display == 'none') {
29918             return Roo.form.FCKeditor.superclass.getValue.call(this);
29919         }
29920         
29921         if(!this.el || !this.getEditor()) {
29922            
29923            // this.getValue.defer(100,this); 
29924             return this.value;
29925         }
29926        
29927         
29928         var value=this.getEditor().GetData();
29929         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
29930         return Roo.form.FCKeditor.superclass.getValue.call(this);
29931         
29932
29933     },
29934
29935     /**
29936      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
29937      * @return {Mixed} value The field value
29938      */
29939     getRawValue : function()
29940     {
29941         if (this.frame && this.frame.dom.style.display == 'none') {
29942             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
29943         }
29944         
29945         if(!this.el || !this.getEditor()) {
29946             //this.getRawValue.defer(100,this); 
29947             return this.value;
29948             return;
29949         }
29950         
29951         
29952         
29953         var value=this.getEditor().GetData();
29954         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
29955         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
29956          
29957     },
29958     
29959     setSize : function(w,h) {
29960         
29961         
29962         
29963         //if (this.frame && this.frame.dom.style.display == 'none') {
29964         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
29965         //    return;
29966         //}
29967         //if(!this.el || !this.getEditor()) {
29968         //    this.setSize.defer(100,this, [w,h]); 
29969         //    return;
29970         //}
29971         
29972         
29973         
29974         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
29975         
29976         this.frame.dom.setAttribute('width', w);
29977         this.frame.dom.setAttribute('height', h);
29978         this.frame.setSize(w,h);
29979         
29980     },
29981     
29982     toggleSourceEdit : function(value) {
29983         
29984       
29985          
29986         this.el.dom.style.display = value ? '' : 'none';
29987         this.frame.dom.style.display = value ?  'none' : '';
29988         
29989     },
29990     
29991     
29992     focus: function(tag)
29993     {
29994         if (this.frame.dom.style.display == 'none') {
29995             return Roo.form.FCKeditor.superclass.focus.call(this);
29996         }
29997         if(!this.el || !this.getEditor()) {
29998             this.focus.defer(100,this, [tag]); 
29999             return;
30000         }
30001         
30002         
30003         
30004         
30005         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
30006         this.getEditor().Focus();
30007         if (tgs.length) {
30008             if (!this.getEditor().Selection.GetSelection()) {
30009                 this.focus.defer(100,this, [tag]); 
30010                 return;
30011             }
30012             
30013             
30014             var r = this.getEditor().EditorDocument.createRange();
30015             r.setStart(tgs[0],0);
30016             r.setEnd(tgs[0],0);
30017             this.getEditor().Selection.GetSelection().removeAllRanges();
30018             this.getEditor().Selection.GetSelection().addRange(r);
30019             this.getEditor().Focus();
30020         }
30021         
30022     },
30023     
30024     
30025     
30026     replaceTextarea : function()
30027     {
30028         if ( document.getElementById( this.getId() + '___Frame' ) )
30029             return ;
30030         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
30031         //{
30032             // We must check the elements firstly using the Id and then the name.
30033         var oTextarea = document.getElementById( this.getId() );
30034         
30035         var colElementsByName = document.getElementsByName( this.getId() ) ;
30036          
30037         oTextarea.style.display = 'none' ;
30038
30039         if ( oTextarea.tabIndex ) {            
30040             this.TabIndex = oTextarea.tabIndex ;
30041         }
30042         
30043         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
30044         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
30045         this.frame = Roo.get(this.getId() + '___Frame')
30046     },
30047     
30048     _getConfigHtml : function()
30049     {
30050         var sConfig = '' ;
30051
30052         for ( var o in this.fckconfig ) {
30053             sConfig += sConfig.length > 0  ? '&amp;' : '';
30054             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
30055         }
30056
30057         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
30058     },
30059     
30060     
30061     _getIFrameHtml : function()
30062     {
30063         var sFile = 'fckeditor.html' ;
30064         /* no idea what this is about..
30065         try
30066         {
30067             if ( (/fcksource=true/i).test( window.top.location.search ) )
30068                 sFile = 'fckeditor.original.html' ;
30069         }
30070         catch (e) { 
30071         */
30072
30073         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
30074         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
30075         
30076         
30077         var html = '<iframe id="' + this.getId() +
30078             '___Frame" src="' + sLink +
30079             '" width="' + this.width +
30080             '" height="' + this.height + '"' +
30081             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
30082             ' frameborder="0" scrolling="no"></iframe>' ;
30083
30084         return html ;
30085     },
30086     
30087     _insertHtmlBefore : function( html, element )
30088     {
30089         if ( element.insertAdjacentHTML )       {
30090             // IE
30091             element.insertAdjacentHTML( 'beforeBegin', html ) ;
30092         } else { // Gecko
30093             var oRange = document.createRange() ;
30094             oRange.setStartBefore( element ) ;
30095             var oFragment = oRange.createContextualFragment( html );
30096             element.parentNode.insertBefore( oFragment, element ) ;
30097         }
30098     }
30099     
30100     
30101   
30102     
30103     
30104     
30105     
30106
30107 });
30108
30109 //Roo.reg('fckeditor', Roo.form.FCKeditor);
30110
30111 function FCKeditor_OnComplete(editorInstance){
30112     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
30113     f.fckEditor = editorInstance;
30114     //console.log("loaded");
30115     f.fireEvent('editorinit', f, editorInstance);
30116
30117   
30118
30119  
30120
30121
30122
30123
30124
30125
30126
30127
30128
30129
30130
30131
30132
30133
30134
30135 //<script type="text/javascript">
30136 /**
30137  * @class Roo.form.GridField
30138  * @extends Roo.form.Field
30139  * Embed a grid (or editable grid into a form)
30140  * STATUS ALPHA
30141  * 
30142  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
30143  * it needs 
30144  * xgrid.store = Roo.data.Store
30145  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
30146  * xgrid.store.reader = Roo.data.JsonReader 
30147  * 
30148  * 
30149  * @constructor
30150  * Creates a new GridField
30151  * @param {Object} config Configuration options
30152  */
30153 Roo.form.GridField = function(config){
30154     Roo.form.GridField.superclass.constructor.call(this, config);
30155      
30156 };
30157
30158 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
30159     /**
30160      * @cfg {Number} width  - used to restrict width of grid..
30161      */
30162     width : 100,
30163     /**
30164      * @cfg {Number} height - used to restrict height of grid..
30165      */
30166     height : 50,
30167      /**
30168      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
30169          * 
30170          *}
30171      */
30172     xgrid : false, 
30173     /**
30174      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30175      * {tag: "input", type: "checkbox", autocomplete: "off"})
30176      */
30177    // defaultAutoCreate : { tag: 'div' },
30178     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
30179     /**
30180      * @cfg {String} addTitle Text to include for adding a title.
30181      */
30182     addTitle : false,
30183     //
30184     onResize : function(){
30185         Roo.form.Field.superclass.onResize.apply(this, arguments);
30186     },
30187
30188     initEvents : function(){
30189         // Roo.form.Checkbox.superclass.initEvents.call(this);
30190         // has no events...
30191        
30192     },
30193
30194
30195     getResizeEl : function(){
30196         return this.wrap;
30197     },
30198
30199     getPositionEl : function(){
30200         return this.wrap;
30201     },
30202
30203     // private
30204     onRender : function(ct, position){
30205         
30206         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
30207         var style = this.style;
30208         delete this.style;
30209         
30210         Roo.form.GridField.superclass.onRender.call(this, ct, position);
30211         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
30212         this.viewEl = this.wrap.createChild({ tag: 'div' });
30213         if (style) {
30214             this.viewEl.applyStyles(style);
30215         }
30216         if (this.width) {
30217             this.viewEl.setWidth(this.width);
30218         }
30219         if (this.height) {
30220             this.viewEl.setHeight(this.height);
30221         }
30222         //if(this.inputValue !== undefined){
30223         //this.setValue(this.value);
30224         
30225         
30226         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
30227         
30228         
30229         this.grid.render();
30230         this.grid.getDataSource().on('remove', this.refreshValue, this);
30231         this.grid.getDataSource().on('update', this.refreshValue, this);
30232         this.grid.on('afteredit', this.refreshValue, this);
30233  
30234     },
30235      
30236     
30237     /**
30238      * Sets the value of the item. 
30239      * @param {String} either an object  or a string..
30240      */
30241     setValue : function(v){
30242         //this.value = v;
30243         v = v || []; // empty set..
30244         // this does not seem smart - it really only affects memoryproxy grids..
30245         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
30246             var ds = this.grid.getDataSource();
30247             // assumes a json reader..
30248             var data = {}
30249             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
30250             ds.loadData( data);
30251         }
30252         // clear selection so it does not get stale.
30253         if (this.grid.sm) { 
30254             this.grid.sm.clearSelections();
30255         }
30256         
30257         Roo.form.GridField.superclass.setValue.call(this, v);
30258         this.refreshValue();
30259         // should load data in the grid really....
30260     },
30261     
30262     // private
30263     refreshValue: function() {
30264          var val = [];
30265         this.grid.getDataSource().each(function(r) {
30266             val.push(r.data);
30267         });
30268         this.el.dom.value = Roo.encode(val);
30269     }
30270     
30271      
30272     
30273     
30274 });/*
30275  * Based on:
30276  * Ext JS Library 1.1.1
30277  * Copyright(c) 2006-2007, Ext JS, LLC.
30278  *
30279  * Originally Released Under LGPL - original licence link has changed is not relivant.
30280  *
30281  * Fork - LGPL
30282  * <script type="text/javascript">
30283  */
30284 /**
30285  * @class Roo.form.DisplayField
30286  * @extends Roo.form.Field
30287  * A generic Field to display non-editable data.
30288  * @constructor
30289  * Creates a new Display Field item.
30290  * @param {Object} config Configuration options
30291  */
30292 Roo.form.DisplayField = function(config){
30293     Roo.form.DisplayField.superclass.constructor.call(this, config);
30294     
30295 };
30296
30297 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
30298     inputType:      'hidden',
30299     allowBlank:     true,
30300     readOnly:         true,
30301     
30302  
30303     /**
30304      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30305      */
30306     focusClass : undefined,
30307     /**
30308      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30309      */
30310     fieldClass: 'x-form-field',
30311     
30312      /**
30313      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
30314      */
30315     valueRenderer: undefined,
30316     
30317     width: 100,
30318     /**
30319      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30320      * {tag: "input", type: "checkbox", autocomplete: "off"})
30321      */
30322      
30323  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
30324
30325     onResize : function(){
30326         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
30327         
30328     },
30329
30330     initEvents : function(){
30331         // Roo.form.Checkbox.superclass.initEvents.call(this);
30332         // has no events...
30333        
30334     },
30335
30336
30337     getResizeEl : function(){
30338         return this.wrap;
30339     },
30340
30341     getPositionEl : function(){
30342         return this.wrap;
30343     },
30344
30345     // private
30346     onRender : function(ct, position){
30347         
30348         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
30349         //if(this.inputValue !== undefined){
30350         this.wrap = this.el.wrap();
30351         
30352         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
30353         
30354         if (this.bodyStyle) {
30355             this.viewEl.applyStyles(this.bodyStyle);
30356         }
30357         //this.viewEl.setStyle('padding', '2px');
30358         
30359         this.setValue(this.value);
30360         
30361     },
30362 /*
30363     // private
30364     initValue : Roo.emptyFn,
30365
30366   */
30367
30368         // private
30369     onClick : function(){
30370         
30371     },
30372
30373     /**
30374      * Sets the checked state of the checkbox.
30375      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
30376      */
30377     setValue : function(v){
30378         this.value = v;
30379         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
30380         // this might be called before we have a dom element..
30381         if (!this.viewEl) {
30382             return;
30383         }
30384         this.viewEl.dom.innerHTML = html;
30385         Roo.form.DisplayField.superclass.setValue.call(this, v);
30386
30387     }
30388 });/*
30389  * 
30390  * Licence- LGPL
30391  * 
30392  */
30393
30394 /**
30395  * @class Roo.form.DayPicker
30396  * @extends Roo.form.Field
30397  * A Day picker show [M] [T] [W] ....
30398  * @constructor
30399  * Creates a new Day Picker
30400  * @param {Object} config Configuration options
30401  */
30402 Roo.form.DayPicker= function(config){
30403     Roo.form.DayPicker.superclass.constructor.call(this, config);
30404      
30405 };
30406
30407 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
30408     /**
30409      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30410      */
30411     focusClass : undefined,
30412     /**
30413      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30414      */
30415     fieldClass: "x-form-field",
30416    
30417     /**
30418      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30419      * {tag: "input", type: "checkbox", autocomplete: "off"})
30420      */
30421     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
30422     
30423    
30424     actionMode : 'viewEl', 
30425     //
30426     // private
30427  
30428     inputType : 'hidden',
30429     
30430      
30431     inputElement: false, // real input element?
30432     basedOn: false, // ????
30433     
30434     isFormField: true, // not sure where this is needed!!!!
30435
30436     onResize : function(){
30437         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
30438         if(!this.boxLabel){
30439             this.el.alignTo(this.wrap, 'c-c');
30440         }
30441     },
30442
30443     initEvents : function(){
30444         Roo.form.Checkbox.superclass.initEvents.call(this);
30445         this.el.on("click", this.onClick,  this);
30446         this.el.on("change", this.onClick,  this);
30447     },
30448
30449
30450     getResizeEl : function(){
30451         return this.wrap;
30452     },
30453
30454     getPositionEl : function(){
30455         return this.wrap;
30456     },
30457
30458     
30459     // private
30460     onRender : function(ct, position){
30461         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
30462        
30463         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
30464         
30465         var r1 = '<table><tr>';
30466         var r2 = '<tr class="x-form-daypick-icons">';
30467         for (var i=0; i < 7; i++) {
30468             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
30469             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
30470         }
30471         
30472         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
30473         viewEl.select('img').on('click', this.onClick, this);
30474         this.viewEl = viewEl;   
30475         
30476         
30477         // this will not work on Chrome!!!
30478         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
30479         this.el.on('propertychange', this.setFromHidden,  this);  //ie
30480         
30481         
30482           
30483
30484     },
30485
30486     // private
30487     initValue : Roo.emptyFn,
30488
30489     /**
30490      * Returns the checked state of the checkbox.
30491      * @return {Boolean} True if checked, else false
30492      */
30493     getValue : function(){
30494         return this.el.dom.value;
30495         
30496     },
30497
30498         // private
30499     onClick : function(e){ 
30500         //this.setChecked(!this.checked);
30501         Roo.get(e.target).toggleClass('x-menu-item-checked');
30502         this.refreshValue();
30503         //if(this.el.dom.checked != this.checked){
30504         //    this.setValue(this.el.dom.checked);
30505        // }
30506     },
30507     
30508     // private
30509     refreshValue : function()
30510     {
30511         var val = '';
30512         this.viewEl.select('img',true).each(function(e,i,n)  {
30513             val += e.is(".x-menu-item-checked") ? String(n) : '';
30514         });
30515         this.setValue(val, true);
30516     },
30517
30518     /**
30519      * Sets the checked state of the checkbox.
30520      * On is always based on a string comparison between inputValue and the param.
30521      * @param {Boolean/String} value - the value to set 
30522      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
30523      */
30524     setValue : function(v,suppressEvent){
30525         if (!this.el.dom) {
30526             return;
30527         }
30528         var old = this.el.dom.value ;
30529         this.el.dom.value = v;
30530         if (suppressEvent) {
30531             return ;
30532         }
30533          
30534         // update display..
30535         this.viewEl.select('img',true).each(function(e,i,n)  {
30536             
30537             var on = e.is(".x-menu-item-checked");
30538             var newv = v.indexOf(String(n)) > -1;
30539             if (on != newv) {
30540                 e.toggleClass('x-menu-item-checked');
30541             }
30542             
30543         });
30544         
30545         
30546         this.fireEvent('change', this, v, old);
30547         
30548         
30549     },
30550    
30551     // handle setting of hidden value by some other method!!?!?
30552     setFromHidden: function()
30553     {
30554         if(!this.el){
30555             return;
30556         }
30557         //console.log("SET FROM HIDDEN");
30558         //alert('setFrom hidden');
30559         this.setValue(this.el.dom.value);
30560     },
30561     
30562     onDestroy : function()
30563     {
30564         if(this.viewEl){
30565             Roo.get(this.viewEl).remove();
30566         }
30567          
30568         Roo.form.DayPicker.superclass.onDestroy.call(this);
30569     }
30570
30571 });/*
30572  * RooJS Library 1.1.1
30573  * Copyright(c) 2008-2011  Alan Knowles
30574  *
30575  * License - LGPL
30576  */
30577  
30578
30579 /**
30580  * @class Roo.form.ComboCheck
30581  * @extends Roo.form.ComboBox
30582  * A combobox for multiple select items.
30583  *
30584  * FIXME - could do with a reset button..
30585  * 
30586  * @constructor
30587  * Create a new ComboCheck
30588  * @param {Object} config Configuration options
30589  */
30590 Roo.form.ComboCheck = function(config){
30591     Roo.form.ComboCheck.superclass.constructor.call(this, config);
30592     // should verify some data...
30593     // like
30594     // hiddenName = required..
30595     // displayField = required
30596     // valudField == required
30597     var req= [ 'hiddenName', 'displayField', 'valueField' ];
30598     var _t = this;
30599     Roo.each(req, function(e) {
30600         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
30601             throw "Roo.form.ComboCheck : missing value for: " + e;
30602         }
30603     });
30604     
30605     
30606 };
30607
30608 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
30609      
30610      
30611     editable : false,
30612      
30613     selectedClass: 'x-menu-item-checked', 
30614     
30615     // private
30616     onRender : function(ct, position){
30617         var _t = this;
30618         
30619         
30620         
30621         if(!this.tpl){
30622             var cls = 'x-combo-list';
30623
30624             
30625             this.tpl =  new Roo.Template({
30626                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
30627                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
30628                    '<span>{' + this.displayField + '}</span>' +
30629                     '</div>' 
30630                 
30631             });
30632         }
30633  
30634         
30635         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
30636         this.view.singleSelect = false;
30637         this.view.multiSelect = true;
30638         this.view.toggleSelect = true;
30639         this.pageTb.add(new Roo.Toolbar.Fill(), {
30640             
30641             text: 'Done',
30642             handler: function()
30643             {
30644                 _t.collapse();
30645             }
30646         });
30647     },
30648     
30649     onViewOver : function(e, t){
30650         // do nothing...
30651         return;
30652         
30653     },
30654     
30655     onViewClick : function(doFocus,index){
30656         return;
30657         
30658     },
30659     select: function () {
30660         //Roo.log("SELECT CALLED");
30661     },
30662      
30663     selectByValue : function(xv, scrollIntoView){
30664         var ar = this.getValueArray();
30665         var sels = [];
30666         
30667         Roo.each(ar, function(v) {
30668             if(v === undefined || v === null){
30669                 return;
30670             }
30671             var r = this.findRecord(this.valueField, v);
30672             if(r){
30673                 sels.push(this.store.indexOf(r))
30674                 
30675             }
30676         },this);
30677         this.view.select(sels);
30678         return false;
30679     },
30680     
30681     
30682     
30683     onSelect : function(record, index){
30684        // Roo.log("onselect Called");
30685        // this is only called by the clear button now..
30686         this.view.clearSelections();
30687         this.setValue('[]');
30688         if (this.value != this.valueBefore) {
30689             this.fireEvent('change', this, this.value, this.valueBefore);
30690             this.valueBefore = this.value;
30691         }
30692     },
30693     getValueArray : function()
30694     {
30695         var ar = [] ;
30696         
30697         try {
30698             //Roo.log(this.value);
30699             if (typeof(this.value) == 'undefined') {
30700                 return [];
30701             }
30702             var ar = Roo.decode(this.value);
30703             return  ar instanceof Array ? ar : []; //?? valid?
30704             
30705         } catch(e) {
30706             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
30707             return [];
30708         }
30709          
30710     },
30711     expand : function ()
30712     {
30713         
30714         Roo.form.ComboCheck.superclass.expand.call(this);
30715         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
30716         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
30717         
30718
30719     },
30720     
30721     collapse : function(){
30722         Roo.form.ComboCheck.superclass.collapse.call(this);
30723         var sl = this.view.getSelectedIndexes();
30724         var st = this.store;
30725         var nv = [];
30726         var tv = [];
30727         var r;
30728         Roo.each(sl, function(i) {
30729             r = st.getAt(i);
30730             nv.push(r.get(this.valueField));
30731         },this);
30732         this.setValue(Roo.encode(nv));
30733         if (this.value != this.valueBefore) {
30734
30735             this.fireEvent('change', this, this.value, this.valueBefore);
30736             this.valueBefore = this.value;
30737         }
30738         
30739     },
30740     
30741     setValue : function(v){
30742         // Roo.log(v);
30743         this.value = v;
30744         
30745         var vals = this.getValueArray();
30746         var tv = [];
30747         Roo.each(vals, function(k) {
30748             var r = this.findRecord(this.valueField, k);
30749             if(r){
30750                 tv.push(r.data[this.displayField]);
30751             }else if(this.valueNotFoundText !== undefined){
30752                 tv.push( this.valueNotFoundText );
30753             }
30754         },this);
30755        // Roo.log(tv);
30756         
30757         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
30758         this.hiddenField.value = v;
30759         this.value = v;
30760     }
30761     
30762 });/*
30763  * Based on:
30764  * Ext JS Library 1.1.1
30765  * Copyright(c) 2006-2007, Ext JS, LLC.
30766  *
30767  * Originally Released Under LGPL - original licence link has changed is not relivant.
30768  *
30769  * Fork - LGPL
30770  * <script type="text/javascript">
30771  */
30772  
30773 /**
30774  * @class Roo.form.Signature
30775  * @extends Roo.form.Field
30776  * Signature field.  
30777  * @constructor
30778  * 
30779  * @param {Object} config Configuration options
30780  */
30781
30782 Roo.form.Signature = function(config){
30783     Roo.form.Signature.superclass.constructor.call(this, config);
30784     
30785     this.addEvents({// not in used??
30786          /**
30787          * @event confirm
30788          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
30789              * @param {Roo.form.Signature} combo This combo box
30790              */
30791         'confirm' : true,
30792         /**
30793          * @event reset
30794          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
30795              * @param {Roo.form.ComboBox} combo This combo box
30796              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
30797              */
30798         'reset' : true
30799     });
30800 };
30801
30802 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
30803     /**
30804      * @cfg {Object} labels Label to use when rendering a form.
30805      * defaults to 
30806      * labels : { 
30807      *      clear : "Clear",
30808      *      confirm : "Confirm"
30809      *  }
30810      */
30811     labels : { 
30812         clear : "Clear",
30813         confirm : "Confirm"
30814     },
30815     /**
30816      * @cfg {Number} width The signature panel width (defaults to 300)
30817      */
30818     width: 300,
30819     /**
30820      * @cfg {Number} height The signature panel height (defaults to 100)
30821      */
30822     height : 100,
30823     /**
30824      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
30825      */
30826     allowBlank : false,
30827     
30828     //private
30829     // {Object} signPanel The signature SVG panel element (defaults to {})
30830     signPanel : {},
30831     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
30832     isMouseDown : false,
30833     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
30834     isConfirmed : false,
30835     // {String} signatureTmp SVG mapping string (defaults to empty string)
30836     signatureTmp : '',
30837     
30838     
30839     defaultAutoCreate : { // modified by initCompnoent..
30840         tag: "input",
30841         type:"hidden"
30842     },
30843
30844     // private
30845     onRender : function(ct, position){
30846         
30847         Roo.form.Signature.superclass.onRender.call(this, ct, position);
30848         
30849         this.wrap = this.el.wrap({
30850             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
30851         });
30852         
30853         this.createToolbar(this);
30854         this.signPanel = this.wrap.createChild({
30855                 tag: 'div',
30856                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
30857             }, this.el
30858         );
30859             
30860         this.svgID = Roo.id();
30861         this.svgEl = this.signPanel.createChild({
30862               xmlns : 'http://www.w3.org/2000/svg',
30863               tag : 'svg',
30864               id : this.svgID + "-svg",
30865               width: this.width,
30866               height: this.height,
30867               viewBox: '0 0 '+this.width+' '+this.height,
30868               cn : [
30869                 {
30870                     tag: "rect",
30871                     id: this.svgID + "-svg-r",
30872                     width: this.width,
30873                     height: this.height,
30874                     fill: "#ffa"
30875                 },
30876                 {
30877                     tag: "line",
30878                     id: this.svgID + "-svg-l",
30879                     x1: "0", // start
30880                     y1: (this.height*0.8), // start set the line in 80% of height
30881                     x2: this.width, // end
30882                     y2: (this.height*0.8), // end set the line in 80% of height
30883                     'stroke': "#666",
30884                     'stroke-width': "1",
30885                     'stroke-dasharray': "3",
30886                     'shape-rendering': "crispEdges",
30887                     'pointer-events': "none"
30888                 },
30889                 {
30890                     tag: "path",
30891                     id: this.svgID + "-svg-p",
30892                     'stroke': "navy",
30893                     'stroke-width': "3",
30894                     'fill': "none",
30895                     'pointer-events': 'none'
30896                 }
30897               ]
30898         });
30899         this.createSVG();
30900         this.svgBox = this.svgEl.dom.getScreenCTM();
30901     },
30902     createSVG : function(){ 
30903         var svg = this.signPanel;
30904         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
30905         var t = this;
30906
30907         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
30908         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
30909         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
30910         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
30911         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
30912         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
30913         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
30914         
30915     },
30916     isTouchEvent : function(e){
30917         return e.type.match(/^touch/);
30918     },
30919     getCoords : function (e) {
30920         var pt    = this.svgEl.dom.createSVGPoint();
30921         pt.x = e.clientX; 
30922         pt.y = e.clientY;
30923         if (this.isTouchEvent(e)) {
30924             pt.x =  e.targetTouches[0].clientX 
30925             pt.y = e.targetTouches[0].clientY;
30926         }
30927         var a = this.svgEl.dom.getScreenCTM();
30928         var b = a.inverse();
30929         var mx = pt.matrixTransform(b);
30930         return mx.x + ',' + mx.y;
30931     },
30932     //mouse event headler 
30933     down : function (e) {
30934         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
30935         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
30936         
30937         this.isMouseDown = true;
30938         
30939         e.preventDefault();
30940     },
30941     move : function (e) {
30942         if (this.isMouseDown) {
30943             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
30944             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
30945         }
30946         
30947         e.preventDefault();
30948     },
30949     up : function (e) {
30950         this.isMouseDown = false;
30951         var sp = this.signatureTmp.split(' ');
30952         
30953         if(sp.length > 1){
30954             if(!sp[sp.length-2].match(/^L/)){
30955                 sp.pop();
30956                 sp.pop();
30957                 sp.push("");
30958                 this.signatureTmp = sp.join(" ");
30959             }
30960         }
30961         if(this.getValue() != this.signatureTmp){
30962             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
30963             this.isConfirmed = false;
30964         }
30965         e.preventDefault();
30966     },
30967     
30968     /**
30969      * Protected method that will not generally be called directly. It
30970      * is called when the editor creates its toolbar. Override this method if you need to
30971      * add custom toolbar buttons.
30972      * @param {HtmlEditor} editor
30973      */
30974     createToolbar : function(editor){
30975          function btn(id, toggle, handler){
30976             var xid = fid + '-'+ id ;
30977             return {
30978                 id : xid,
30979                 cmd : id,
30980                 cls : 'x-btn-icon x-edit-'+id,
30981                 enableToggle:toggle !== false,
30982                 scope: editor, // was editor...
30983                 handler:handler||editor.relayBtnCmd,
30984                 clickEvent:'mousedown',
30985                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
30986                 tabIndex:-1
30987             };
30988         }
30989         
30990         
30991         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
30992         this.tb = tb;
30993         this.tb.add(
30994            {
30995                 cls : ' x-signature-btn x-signature-'+id,
30996                 scope: editor, // was editor...
30997                 handler: this.reset,
30998                 clickEvent:'mousedown',
30999                 text: this.labels.clear
31000             },
31001             {
31002                  xtype : 'Fill',
31003                  xns: Roo.Toolbar
31004             }, 
31005             {
31006                 cls : '  x-signature-btn x-signature-'+id,
31007                 scope: editor, // was editor...
31008                 handler: this.confirmHandler,
31009                 clickEvent:'mousedown',
31010                 text: this.labels.confirm
31011             }
31012         );
31013     
31014     },
31015     //public
31016     /**
31017      * when user is clicked confirm then show this image.....
31018      * 
31019      * @return {String} Image Data URI
31020      */
31021     getImageDataURI : function(){
31022         var svg = this.svgEl.dom.parentNode.innerHTML;
31023         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
31024         return src; 
31025     },
31026     /**
31027      * 
31028      * @return {Boolean} this.isConfirmed
31029      */
31030     getConfirmed : function(){
31031         return this.isConfirmed;
31032     },
31033     /**
31034      * 
31035      * @return {Number} this.width
31036      */
31037     getWidth : function(){
31038         return this.width;
31039     },
31040     /**
31041      * 
31042      * @return {Number} this.height
31043      */
31044     getHeight : function(){
31045         return this.height;
31046     },
31047     // private
31048     getSignature : function(){
31049         return this.signatureTmp;
31050     },
31051     // private
31052     reset : function(){
31053         this.signatureTmp = '';
31054         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31055         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
31056         this.isConfirmed = false;
31057         Roo.form.Signature.superclass.reset.call(this);
31058     },
31059     setSignature : function(s){
31060         this.signatureTmp = s;
31061         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31062         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
31063         this.setValue(s);
31064         this.isConfirmed = false;
31065         Roo.form.Signature.superclass.reset.call(this);
31066     }, 
31067     test : function(){
31068 //        Roo.log(this.signPanel.dom.contentWindow.up())
31069     },
31070     //private
31071     setConfirmed : function(){
31072         
31073         
31074         
31075 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
31076     },
31077     // private
31078     confirmHandler : function(){
31079         if(!this.getSignature()){
31080             return;
31081         }
31082         
31083         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
31084         this.setValue(this.getSignature());
31085         this.isConfirmed = true;
31086         
31087         this.fireEvent('confirm', this);
31088     },
31089     // private
31090     // Subclasses should provide the validation implementation by overriding this
31091     validateValue : function(value){
31092         if(this.allowBlank){
31093             return true;
31094         }
31095         
31096         if(this.isConfirmed){
31097             return true;
31098         }
31099         return false;
31100     }
31101 });//<script type="text/javasscript">
31102  
31103
31104 /**
31105  * @class Roo.DDView
31106  * A DnD enabled version of Roo.View.
31107  * @param {Element/String} container The Element in which to create the View.
31108  * @param {String} tpl The template string used to create the markup for each element of the View
31109  * @param {Object} config The configuration properties. These include all the config options of
31110  * {@link Roo.View} plus some specific to this class.<br>
31111  * <p>
31112  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
31113  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
31114  * <p>
31115  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
31116 .x-view-drag-insert-above {
31117         border-top:1px dotted #3366cc;
31118 }
31119 .x-view-drag-insert-below {
31120         border-bottom:1px dotted #3366cc;
31121 }
31122 </code></pre>
31123  * 
31124  */
31125  
31126 Roo.DDView = function(container, tpl, config) {
31127     Roo.DDView.superclass.constructor.apply(this, arguments);
31128     this.getEl().setStyle("outline", "0px none");
31129     this.getEl().unselectable();
31130     if (this.dragGroup) {
31131                 this.setDraggable(this.dragGroup.split(","));
31132     }
31133     if (this.dropGroup) {
31134                 this.setDroppable(this.dropGroup.split(","));
31135     }
31136     if (this.deletable) {
31137         this.setDeletable();
31138     }
31139     this.isDirtyFlag = false;
31140         this.addEvents({
31141                 "drop" : true
31142         });
31143 };
31144
31145 Roo.extend(Roo.DDView, Roo.View, {
31146 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
31147 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
31148 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
31149 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
31150
31151         isFormField: true,
31152
31153         reset: Roo.emptyFn,
31154         
31155         clearInvalid: Roo.form.Field.prototype.clearInvalid,
31156
31157         validate: function() {
31158                 return true;
31159         },
31160         
31161         destroy: function() {
31162                 this.purgeListeners();
31163                 this.getEl.removeAllListeners();
31164                 this.getEl().remove();
31165                 if (this.dragZone) {
31166                         if (this.dragZone.destroy) {
31167                                 this.dragZone.destroy();
31168                         }
31169                 }
31170                 if (this.dropZone) {
31171                         if (this.dropZone.destroy) {
31172                                 this.dropZone.destroy();
31173                         }
31174                 }
31175         },
31176
31177 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
31178         getName: function() {
31179                 return this.name;
31180         },
31181
31182 /**     Loads the View from a JSON string representing the Records to put into the Store. */
31183         setValue: function(v) {
31184                 if (!this.store) {
31185                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
31186                 }
31187                 var data = {};
31188                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
31189                 this.store.proxy = new Roo.data.MemoryProxy(data);
31190                 this.store.load();
31191         },
31192
31193 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
31194         getValue: function() {
31195                 var result = '(';
31196                 this.store.each(function(rec) {
31197                         result += rec.id + ',';
31198                 });
31199                 return result.substr(0, result.length - 1) + ')';
31200         },
31201         
31202         getIds: function() {
31203                 var i = 0, result = new Array(this.store.getCount());
31204                 this.store.each(function(rec) {
31205                         result[i++] = rec.id;
31206                 });
31207                 return result;
31208         },
31209         
31210         isDirty: function() {
31211                 return this.isDirtyFlag;
31212         },
31213
31214 /**
31215  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
31216  *      whole Element becomes the target, and this causes the drop gesture to append.
31217  */
31218     getTargetFromEvent : function(e) {
31219                 var target = e.getTarget();
31220                 while ((target !== null) && (target.parentNode != this.el.dom)) {
31221                 target = target.parentNode;
31222                 }
31223                 if (!target) {
31224                         target = this.el.dom.lastChild || this.el.dom;
31225                 }
31226                 return target;
31227     },
31228
31229 /**
31230  *      Create the drag data which consists of an object which has the property "ddel" as
31231  *      the drag proxy element. 
31232  */
31233     getDragData : function(e) {
31234         var target = this.findItemFromChild(e.getTarget());
31235                 if(target) {
31236                         this.handleSelection(e);
31237                         var selNodes = this.getSelectedNodes();
31238             var dragData = {
31239                 source: this,
31240                 copy: this.copy || (this.allowCopy && e.ctrlKey),
31241                 nodes: selNodes,
31242                 records: []
31243                         };
31244                         var selectedIndices = this.getSelectedIndexes();
31245                         for (var i = 0; i < selectedIndices.length; i++) {
31246                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
31247                         }
31248                         if (selNodes.length == 1) {
31249                                 dragData.ddel = target.cloneNode(true); // the div element
31250                         } else {
31251                                 var div = document.createElement('div'); // create the multi element drag "ghost"
31252                                 div.className = 'multi-proxy';
31253                                 for (var i = 0, len = selNodes.length; i < len; i++) {
31254                                         div.appendChild(selNodes[i].cloneNode(true));
31255                                 }
31256                                 dragData.ddel = div;
31257                         }
31258             //console.log(dragData)
31259             //console.log(dragData.ddel.innerHTML)
31260                         return dragData;
31261                 }
31262         //console.log('nodragData')
31263                 return false;
31264     },
31265     
31266 /**     Specify to which ddGroup items in this DDView may be dragged. */
31267     setDraggable: function(ddGroup) {
31268         if (ddGroup instanceof Array) {
31269                 Roo.each(ddGroup, this.setDraggable, this);
31270                 return;
31271         }
31272         if (this.dragZone) {
31273                 this.dragZone.addToGroup(ddGroup);
31274         } else {
31275                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
31276                                 containerScroll: true,
31277                                 ddGroup: ddGroup 
31278
31279                         });
31280 //                      Draggability implies selection. DragZone's mousedown selects the element.
31281                         if (!this.multiSelect) { this.singleSelect = true; }
31282
31283 //                      Wire the DragZone's handlers up to methods in *this*
31284                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
31285                 }
31286     },
31287
31288 /**     Specify from which ddGroup this DDView accepts drops. */
31289     setDroppable: function(ddGroup) {
31290         if (ddGroup instanceof Array) {
31291                 Roo.each(ddGroup, this.setDroppable, this);
31292                 return;
31293         }
31294         if (this.dropZone) {
31295                 this.dropZone.addToGroup(ddGroup);
31296         } else {
31297                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
31298                                 containerScroll: true,
31299                                 ddGroup: ddGroup
31300                         });
31301
31302 //                      Wire the DropZone's handlers up to methods in *this*
31303                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
31304                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
31305                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
31306                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
31307                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
31308                 }
31309     },
31310
31311 /**     Decide whether to drop above or below a View node. */
31312     getDropPoint : function(e, n, dd){
31313         if (n == this.el.dom) { return "above"; }
31314                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
31315                 var c = t + (b - t) / 2;
31316                 var y = Roo.lib.Event.getPageY(e);
31317                 if(y <= c) {
31318                         return "above";
31319                 }else{
31320                         return "below";
31321                 }
31322     },
31323
31324     onNodeEnter : function(n, dd, e, data){
31325                 return false;
31326     },
31327     
31328     onNodeOver : function(n, dd, e, data){
31329                 var pt = this.getDropPoint(e, n, dd);
31330                 // set the insert point style on the target node
31331                 var dragElClass = this.dropNotAllowed;
31332                 if (pt) {
31333                         var targetElClass;
31334                         if (pt == "above"){
31335                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
31336                                 targetElClass = "x-view-drag-insert-above";
31337                         } else {
31338                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
31339                                 targetElClass = "x-view-drag-insert-below";
31340                         }
31341                         if (this.lastInsertClass != targetElClass){
31342                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
31343                                 this.lastInsertClass = targetElClass;
31344                         }
31345                 }
31346                 return dragElClass;
31347         },
31348
31349     onNodeOut : function(n, dd, e, data){
31350                 this.removeDropIndicators(n);
31351     },
31352
31353     onNodeDrop : function(n, dd, e, data){
31354         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
31355                 return false;
31356         }
31357         var pt = this.getDropPoint(e, n, dd);
31358                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
31359                 if (pt == "below") { insertAt++; }
31360                 for (var i = 0; i < data.records.length; i++) {
31361                         var r = data.records[i];
31362                         var dup = this.store.getById(r.id);
31363                         if (dup && (dd != this.dragZone)) {
31364                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
31365                         } else {
31366                                 if (data.copy) {
31367                                         this.store.insert(insertAt++, r.copy());
31368                                 } else {
31369                                         data.source.isDirtyFlag = true;
31370                                         r.store.remove(r);
31371                                         this.store.insert(insertAt++, r);
31372                                 }
31373                                 this.isDirtyFlag = true;
31374                         }
31375                 }
31376                 this.dragZone.cachedTarget = null;
31377                 return true;
31378     },
31379
31380     removeDropIndicators : function(n){
31381                 if(n){
31382                         Roo.fly(n).removeClass([
31383                                 "x-view-drag-insert-above",
31384                                 "x-view-drag-insert-below"]);
31385                         this.lastInsertClass = "_noclass";
31386                 }
31387     },
31388
31389 /**
31390  *      Utility method. Add a delete option to the DDView's context menu.
31391  *      @param {String} imageUrl The URL of the "delete" icon image.
31392  */
31393         setDeletable: function(imageUrl) {
31394                 if (!this.singleSelect && !this.multiSelect) {
31395                         this.singleSelect = true;
31396                 }
31397                 var c = this.getContextMenu();
31398                 this.contextMenu.on("itemclick", function(item) {
31399                         switch (item.id) {
31400                                 case "delete":
31401                                         this.remove(this.getSelectedIndexes());
31402                                         break;
31403                         }
31404                 }, this);
31405                 this.contextMenu.add({
31406                         icon: imageUrl,
31407                         id: "delete",
31408                         text: 'Delete'
31409                 });
31410         },
31411         
31412 /**     Return the context menu for this DDView. */
31413         getContextMenu: function() {
31414                 if (!this.contextMenu) {
31415 //                      Create the View's context menu
31416                         this.contextMenu = new Roo.menu.Menu({
31417                                 id: this.id + "-contextmenu"
31418                         });
31419                         this.el.on("contextmenu", this.showContextMenu, this);
31420                 }
31421                 return this.contextMenu;
31422         },
31423         
31424         disableContextMenu: function() {
31425                 if (this.contextMenu) {
31426                         this.el.un("contextmenu", this.showContextMenu, this);
31427                 }
31428         },
31429
31430         showContextMenu: function(e, item) {
31431         item = this.findItemFromChild(e.getTarget());
31432                 if (item) {
31433                         e.stopEvent();
31434                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
31435                         this.contextMenu.showAt(e.getXY());
31436             }
31437     },
31438
31439 /**
31440  *      Remove {@link Roo.data.Record}s at the specified indices.
31441  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
31442  */
31443     remove: function(selectedIndices) {
31444                 selectedIndices = [].concat(selectedIndices);
31445                 for (var i = 0; i < selectedIndices.length; i++) {
31446                         var rec = this.store.getAt(selectedIndices[i]);
31447                         this.store.remove(rec);
31448                 }
31449     },
31450
31451 /**
31452  *      Double click fires the event, but also, if this is draggable, and there is only one other
31453  *      related DropZone, it transfers the selected node.
31454  */
31455     onDblClick : function(e){
31456         var item = this.findItemFromChild(e.getTarget());
31457         if(item){
31458             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
31459                 return false;
31460             }
31461             if (this.dragGroup) {
31462                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
31463                     while (targets.indexOf(this.dropZone) > -1) {
31464                             targets.remove(this.dropZone);
31465                                 }
31466                     if (targets.length == 1) {
31467                                         this.dragZone.cachedTarget = null;
31468                         var el = Roo.get(targets[0].getEl());
31469                         var box = el.getBox(true);
31470                         targets[0].onNodeDrop(el.dom, {
31471                                 target: el.dom,
31472                                 xy: [box.x, box.y + box.height - 1]
31473                         }, null, this.getDragData(e));
31474                     }
31475                 }
31476         }
31477     },
31478     
31479     handleSelection: function(e) {
31480                 this.dragZone.cachedTarget = null;
31481         var item = this.findItemFromChild(e.getTarget());
31482         if (!item) {
31483                 this.clearSelections(true);
31484                 return;
31485         }
31486                 if (item && (this.multiSelect || this.singleSelect)){
31487                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
31488                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
31489                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
31490                                 this.unselect(item);
31491                         } else {
31492                                 this.select(item, this.multiSelect && e.ctrlKey);
31493                                 this.lastSelection = item;
31494                         }
31495                 }
31496     },
31497
31498     onItemClick : function(item, index, e){
31499                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
31500                         return false;
31501                 }
31502                 return true;
31503     },
31504
31505     unselect : function(nodeInfo, suppressEvent){
31506                 var node = this.getNode(nodeInfo);
31507                 if(node && this.isSelected(node)){
31508                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
31509                                 Roo.fly(node).removeClass(this.selectedClass);
31510                                 this.selections.remove(node);
31511                                 if(!suppressEvent){
31512                                         this.fireEvent("selectionchange", this, this.selections);
31513                                 }
31514                         }
31515                 }
31516     }
31517 });
31518 /*
31519  * Based on:
31520  * Ext JS Library 1.1.1
31521  * Copyright(c) 2006-2007, Ext JS, LLC.
31522  *
31523  * Originally Released Under LGPL - original licence link has changed is not relivant.
31524  *
31525  * Fork - LGPL
31526  * <script type="text/javascript">
31527  */
31528  
31529 /**
31530  * @class Roo.LayoutManager
31531  * @extends Roo.util.Observable
31532  * Base class for layout managers.
31533  */
31534 Roo.LayoutManager = function(container, config){
31535     Roo.LayoutManager.superclass.constructor.call(this);
31536     this.el = Roo.get(container);
31537     // ie scrollbar fix
31538     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
31539         document.body.scroll = "no";
31540     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
31541         this.el.position('relative');
31542     }
31543     this.id = this.el.id;
31544     this.el.addClass("x-layout-container");
31545     /** false to disable window resize monitoring @type Boolean */
31546     this.monitorWindowResize = true;
31547     this.regions = {};
31548     this.addEvents({
31549         /**
31550          * @event layout
31551          * Fires when a layout is performed. 
31552          * @param {Roo.LayoutManager} this
31553          */
31554         "layout" : true,
31555         /**
31556          * @event regionresized
31557          * Fires when the user resizes a region. 
31558          * @param {Roo.LayoutRegion} region The resized region
31559          * @param {Number} newSize The new size (width for east/west, height for north/south)
31560          */
31561         "regionresized" : true,
31562         /**
31563          * @event regioncollapsed
31564          * Fires when a region is collapsed. 
31565          * @param {Roo.LayoutRegion} region The collapsed region
31566          */
31567         "regioncollapsed" : true,
31568         /**
31569          * @event regionexpanded
31570          * Fires when a region is expanded.  
31571          * @param {Roo.LayoutRegion} region The expanded region
31572          */
31573         "regionexpanded" : true
31574     });
31575     this.updating = false;
31576     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
31577 };
31578
31579 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
31580     /**
31581      * Returns true if this layout is currently being updated
31582      * @return {Boolean}
31583      */
31584     isUpdating : function(){
31585         return this.updating; 
31586     },
31587     
31588     /**
31589      * Suspend the LayoutManager from doing auto-layouts while
31590      * making multiple add or remove calls
31591      */
31592     beginUpdate : function(){
31593         this.updating = true;    
31594     },
31595     
31596     /**
31597      * Restore auto-layouts and optionally disable the manager from performing a layout
31598      * @param {Boolean} noLayout true to disable a layout update 
31599      */
31600     endUpdate : function(noLayout){
31601         this.updating = false;
31602         if(!noLayout){
31603             this.layout();
31604         }    
31605     },
31606     
31607     layout: function(){
31608         
31609     },
31610     
31611     onRegionResized : function(region, newSize){
31612         this.fireEvent("regionresized", region, newSize);
31613         this.layout();
31614     },
31615     
31616     onRegionCollapsed : function(region){
31617         this.fireEvent("regioncollapsed", region);
31618     },
31619     
31620     onRegionExpanded : function(region){
31621         this.fireEvent("regionexpanded", region);
31622     },
31623         
31624     /**
31625      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
31626      * performs box-model adjustments.
31627      * @return {Object} The size as an object {width: (the width), height: (the height)}
31628      */
31629     getViewSize : function(){
31630         var size;
31631         if(this.el.dom != document.body){
31632             size = this.el.getSize();
31633         }else{
31634             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
31635         }
31636         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
31637         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
31638         return size;
31639     },
31640     
31641     /**
31642      * Returns the Element this layout is bound to.
31643      * @return {Roo.Element}
31644      */
31645     getEl : function(){
31646         return this.el;
31647     },
31648     
31649     /**
31650      * Returns the specified region.
31651      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
31652      * @return {Roo.LayoutRegion}
31653      */
31654     getRegion : function(target){
31655         return this.regions[target.toLowerCase()];
31656     },
31657     
31658     onWindowResize : function(){
31659         if(this.monitorWindowResize){
31660             this.layout();
31661         }
31662     }
31663 });/*
31664  * Based on:
31665  * Ext JS Library 1.1.1
31666  * Copyright(c) 2006-2007, Ext JS, LLC.
31667  *
31668  * Originally Released Under LGPL - original licence link has changed is not relivant.
31669  *
31670  * Fork - LGPL
31671  * <script type="text/javascript">
31672  */
31673 /**
31674  * @class Roo.BorderLayout
31675  * @extends Roo.LayoutManager
31676  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
31677  * please see: <br><br>
31678  * <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>
31679  * <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>
31680  * Example:
31681  <pre><code>
31682  var layout = new Roo.BorderLayout(document.body, {
31683     north: {
31684         initialSize: 25,
31685         titlebar: false
31686     },
31687     west: {
31688         split:true,
31689         initialSize: 200,
31690         minSize: 175,
31691         maxSize: 400,
31692         titlebar: true,
31693         collapsible: true
31694     },
31695     east: {
31696         split:true,
31697         initialSize: 202,
31698         minSize: 175,
31699         maxSize: 400,
31700         titlebar: true,
31701         collapsible: true
31702     },
31703     south: {
31704         split:true,
31705         initialSize: 100,
31706         minSize: 100,
31707         maxSize: 200,
31708         titlebar: true,
31709         collapsible: true
31710     },
31711     center: {
31712         titlebar: true,
31713         autoScroll:true,
31714         resizeTabs: true,
31715         minTabWidth: 50,
31716         preferredTabWidth: 150
31717     }
31718 });
31719
31720 // shorthand
31721 var CP = Roo.ContentPanel;
31722
31723 layout.beginUpdate();
31724 layout.add("north", new CP("north", "North"));
31725 layout.add("south", new CP("south", {title: "South", closable: true}));
31726 layout.add("west", new CP("west", {title: "West"}));
31727 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
31728 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
31729 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
31730 layout.getRegion("center").showPanel("center1");
31731 layout.endUpdate();
31732 </code></pre>
31733
31734 <b>The container the layout is rendered into can be either the body element or any other element.
31735 If it is not the body element, the container needs to either be an absolute positioned element,
31736 or you will need to add "position:relative" to the css of the container.  You will also need to specify
31737 the container size if it is not the body element.</b>
31738
31739 * @constructor
31740 * Create a new BorderLayout
31741 * @param {String/HTMLElement/Element} container The container this layout is bound to
31742 * @param {Object} config Configuration options
31743  */
31744 Roo.BorderLayout = function(container, config){
31745     config = config || {};
31746     Roo.BorderLayout.superclass.constructor.call(this, container, config);
31747     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
31748     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
31749         var target = this.factory.validRegions[i];
31750         if(config[target]){
31751             this.addRegion(target, config[target]);
31752         }
31753     }
31754 };
31755
31756 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
31757     /**
31758      * Creates and adds a new region if it doesn't already exist.
31759      * @param {String} target The target region key (north, south, east, west or center).
31760      * @param {Object} config The regions config object
31761      * @return {BorderLayoutRegion} The new region
31762      */
31763     addRegion : function(target, config){
31764         if(!this.regions[target]){
31765             var r = this.factory.create(target, this, config);
31766             this.bindRegion(target, r);
31767         }
31768         return this.regions[target];
31769     },
31770
31771     // private (kinda)
31772     bindRegion : function(name, r){
31773         this.regions[name] = r;
31774         r.on("visibilitychange", this.layout, this);
31775         r.on("paneladded", this.layout, this);
31776         r.on("panelremoved", this.layout, this);
31777         r.on("invalidated", this.layout, this);
31778         r.on("resized", this.onRegionResized, this);
31779         r.on("collapsed", this.onRegionCollapsed, this);
31780         r.on("expanded", this.onRegionExpanded, this);
31781     },
31782
31783     /**
31784      * Performs a layout update.
31785      */
31786     layout : function(){
31787         if(this.updating) return;
31788         var size = this.getViewSize();
31789         var w = size.width;
31790         var h = size.height;
31791         var centerW = w;
31792         var centerH = h;
31793         var centerY = 0;
31794         var centerX = 0;
31795         //var x = 0, y = 0;
31796
31797         var rs = this.regions;
31798         var north = rs["north"];
31799         var south = rs["south"]; 
31800         var west = rs["west"];
31801         var east = rs["east"];
31802         var center = rs["center"];
31803         //if(this.hideOnLayout){ // not supported anymore
31804             //c.el.setStyle("display", "none");
31805         //}
31806         if(north && north.isVisible()){
31807             var b = north.getBox();
31808             var m = north.getMargins();
31809             b.width = w - (m.left+m.right);
31810             b.x = m.left;
31811             b.y = m.top;
31812             centerY = b.height + b.y + m.bottom;
31813             centerH -= centerY;
31814             north.updateBox(this.safeBox(b));
31815         }
31816         if(south && south.isVisible()){
31817             var b = south.getBox();
31818             var m = south.getMargins();
31819             b.width = w - (m.left+m.right);
31820             b.x = m.left;
31821             var totalHeight = (b.height + m.top + m.bottom);
31822             b.y = h - totalHeight + m.top;
31823             centerH -= totalHeight;
31824             south.updateBox(this.safeBox(b));
31825         }
31826         if(west && west.isVisible()){
31827             var b = west.getBox();
31828             var m = west.getMargins();
31829             b.height = centerH - (m.top+m.bottom);
31830             b.x = m.left;
31831             b.y = centerY + m.top;
31832             var totalWidth = (b.width + m.left + m.right);
31833             centerX += totalWidth;
31834             centerW -= totalWidth;
31835             west.updateBox(this.safeBox(b));
31836         }
31837         if(east && east.isVisible()){
31838             var b = east.getBox();
31839             var m = east.getMargins();
31840             b.height = centerH - (m.top+m.bottom);
31841             var totalWidth = (b.width + m.left + m.right);
31842             b.x = w - totalWidth + m.left;
31843             b.y = centerY + m.top;
31844             centerW -= totalWidth;
31845             east.updateBox(this.safeBox(b));
31846         }
31847         if(center){
31848             var m = center.getMargins();
31849             var centerBox = {
31850                 x: centerX + m.left,
31851                 y: centerY + m.top,
31852                 width: centerW - (m.left+m.right),
31853                 height: centerH - (m.top+m.bottom)
31854             };
31855             //if(this.hideOnLayout){
31856                 //center.el.setStyle("display", "block");
31857             //}
31858             center.updateBox(this.safeBox(centerBox));
31859         }
31860         this.el.repaint();
31861         this.fireEvent("layout", this);
31862     },
31863
31864     // private
31865     safeBox : function(box){
31866         box.width = Math.max(0, box.width);
31867         box.height = Math.max(0, box.height);
31868         return box;
31869     },
31870
31871     /**
31872      * Adds a ContentPanel (or subclass) to this layout.
31873      * @param {String} target The target region key (north, south, east, west or center).
31874      * @param {Roo.ContentPanel} panel The panel to add
31875      * @return {Roo.ContentPanel} The added panel
31876      */
31877     add : function(target, panel){
31878          
31879         target = target.toLowerCase();
31880         return this.regions[target].add(panel);
31881     },
31882
31883     /**
31884      * Remove a ContentPanel (or subclass) to this layout.
31885      * @param {String} target The target region key (north, south, east, west or center).
31886      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
31887      * @return {Roo.ContentPanel} The removed panel
31888      */
31889     remove : function(target, panel){
31890         target = target.toLowerCase();
31891         return this.regions[target].remove(panel);
31892     },
31893
31894     /**
31895      * Searches all regions for a panel with the specified id
31896      * @param {String} panelId
31897      * @return {Roo.ContentPanel} The panel or null if it wasn't found
31898      */
31899     findPanel : function(panelId){
31900         var rs = this.regions;
31901         for(var target in rs){
31902             if(typeof rs[target] != "function"){
31903                 var p = rs[target].getPanel(panelId);
31904                 if(p){
31905                     return p;
31906                 }
31907             }
31908         }
31909         return null;
31910     },
31911
31912     /**
31913      * Searches all regions for a panel with the specified id and activates (shows) it.
31914      * @param {String/ContentPanel} panelId The panels id or the panel itself
31915      * @return {Roo.ContentPanel} The shown panel or null
31916      */
31917     showPanel : function(panelId) {
31918       var rs = this.regions;
31919       for(var target in rs){
31920          var r = rs[target];
31921          if(typeof r != "function"){
31922             if(r.hasPanel(panelId)){
31923                return r.showPanel(panelId);
31924             }
31925          }
31926       }
31927       return null;
31928    },
31929
31930    /**
31931      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
31932      * @param {Roo.state.Provider} provider (optional) An alternate state provider
31933      */
31934     restoreState : function(provider){
31935         if(!provider){
31936             provider = Roo.state.Manager;
31937         }
31938         var sm = new Roo.LayoutStateManager();
31939         sm.init(this, provider);
31940     },
31941
31942     /**
31943      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
31944      * object should contain properties for each region to add ContentPanels to, and each property's value should be
31945      * a valid ContentPanel config object.  Example:
31946      * <pre><code>
31947 // Create the main layout
31948 var layout = new Roo.BorderLayout('main-ct', {
31949     west: {
31950         split:true,
31951         minSize: 175,
31952         titlebar: true
31953     },
31954     center: {
31955         title:'Components'
31956     }
31957 }, 'main-ct');
31958
31959 // Create and add multiple ContentPanels at once via configs
31960 layout.batchAdd({
31961    west: {
31962        id: 'source-files',
31963        autoCreate:true,
31964        title:'Ext Source Files',
31965        autoScroll:true,
31966        fitToFrame:true
31967    },
31968    center : {
31969        el: cview,
31970        autoScroll:true,
31971        fitToFrame:true,
31972        toolbar: tb,
31973        resizeEl:'cbody'
31974    }
31975 });
31976 </code></pre>
31977      * @param {Object} regions An object containing ContentPanel configs by region name
31978      */
31979     batchAdd : function(regions){
31980         this.beginUpdate();
31981         for(var rname in regions){
31982             var lr = this.regions[rname];
31983             if(lr){
31984                 this.addTypedPanels(lr, regions[rname]);
31985             }
31986         }
31987         this.endUpdate();
31988     },
31989
31990     // private
31991     addTypedPanels : function(lr, ps){
31992         if(typeof ps == 'string'){
31993             lr.add(new Roo.ContentPanel(ps));
31994         }
31995         else if(ps instanceof Array){
31996             for(var i =0, len = ps.length; i < len; i++){
31997                 this.addTypedPanels(lr, ps[i]);
31998             }
31999         }
32000         else if(!ps.events){ // raw config?
32001             var el = ps.el;
32002             delete ps.el; // prevent conflict
32003             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
32004         }
32005         else {  // panel object assumed!
32006             lr.add(ps);
32007         }
32008     },
32009     /**
32010      * Adds a xtype elements to the layout.
32011      * <pre><code>
32012
32013 layout.addxtype({
32014        xtype : 'ContentPanel',
32015        region: 'west',
32016        items: [ .... ]
32017    }
32018 );
32019
32020 layout.addxtype({
32021         xtype : 'NestedLayoutPanel',
32022         region: 'west',
32023         layout: {
32024            center: { },
32025            west: { }   
32026         },
32027         items : [ ... list of content panels or nested layout panels.. ]
32028    }
32029 );
32030 </code></pre>
32031      * @param {Object} cfg Xtype definition of item to add.
32032      */
32033     addxtype : function(cfg)
32034     {
32035         // basically accepts a pannel...
32036         // can accept a layout region..!?!?
32037         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
32038         
32039         if (!cfg.xtype.match(/Panel$/)) {
32040             return false;
32041         }
32042         var ret = false;
32043         
32044         if (typeof(cfg.region) == 'undefined') {
32045             Roo.log("Failed to add Panel, region was not set");
32046             Roo.log(cfg);
32047             return false;
32048         }
32049         var region = cfg.region;
32050         delete cfg.region;
32051         
32052           
32053         var xitems = [];
32054         if (cfg.items) {
32055             xitems = cfg.items;
32056             delete cfg.items;
32057         }
32058         var nb = false;
32059         
32060         switch(cfg.xtype) 
32061         {
32062             case 'ContentPanel':  // ContentPanel (el, cfg)
32063             case 'ScrollPanel':  // ContentPanel (el, cfg)
32064             case 'ViewPanel': 
32065                 if(cfg.autoCreate) {
32066                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32067                 } else {
32068                     var el = this.el.createChild();
32069                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
32070                 }
32071                 
32072                 this.add(region, ret);
32073                 break;
32074             
32075             
32076             case 'TreePanel': // our new panel!
32077                 cfg.el = this.el.createChild();
32078                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32079                 this.add(region, ret);
32080                 break;
32081             
32082             case 'NestedLayoutPanel': 
32083                 // create a new Layout (which is  a Border Layout...
32084                 var el = this.el.createChild();
32085                 var clayout = cfg.layout;
32086                 delete cfg.layout;
32087                 clayout.items   = clayout.items  || [];
32088                 // replace this exitems with the clayout ones..
32089                 xitems = clayout.items;
32090                  
32091                 
32092                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
32093                     cfg.background = false;
32094                 }
32095                 var layout = new Roo.BorderLayout(el, clayout);
32096                 
32097                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
32098                 //console.log('adding nested layout panel '  + cfg.toSource());
32099                 this.add(region, ret);
32100                 nb = {}; /// find first...
32101                 break;
32102                 
32103             case 'GridPanel': 
32104             
32105                 // needs grid and region
32106                 
32107                 //var el = this.getRegion(region).el.createChild();
32108                 var el = this.el.createChild();
32109                 // create the grid first...
32110                 
32111                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
32112                 delete cfg.grid;
32113                 if (region == 'center' && this.active ) {
32114                     cfg.background = false;
32115                 }
32116                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
32117                 
32118                 this.add(region, ret);
32119                 if (cfg.background) {
32120                     ret.on('activate', function(gp) {
32121                         if (!gp.grid.rendered) {
32122                             gp.grid.render();
32123                         }
32124                     });
32125                 } else {
32126                     grid.render();
32127                 }
32128                 break;
32129            
32130            
32131            
32132                 
32133                 
32134                 
32135             default: 
32136                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
32137                 return null;
32138              // GridPanel (grid, cfg)
32139             
32140         }
32141         this.beginUpdate();
32142         // add children..
32143         var region = '';
32144         var abn = {};
32145         Roo.each(xitems, function(i)  {
32146             region = nb && i.region ? i.region : false;
32147             
32148             var add = ret.addxtype(i);
32149            
32150             if (region) {
32151                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
32152                 if (!i.background) {
32153                     abn[region] = nb[region] ;
32154                 }
32155             }
32156             
32157         });
32158         this.endUpdate();
32159
32160         // make the last non-background panel active..
32161         //if (nb) { Roo.log(abn); }
32162         if (nb) {
32163             
32164             for(var r in abn) {
32165                 region = this.getRegion(r);
32166                 if (region) {
32167                     // tried using nb[r], but it does not work..
32168                      
32169                     region.showPanel(abn[r]);
32170                    
32171                 }
32172             }
32173         }
32174         return ret;
32175         
32176     }
32177 });
32178
32179 /**
32180  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
32181  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
32182  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
32183  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
32184  * <pre><code>
32185 // shorthand
32186 var CP = Roo.ContentPanel;
32187
32188 var layout = Roo.BorderLayout.create({
32189     north: {
32190         initialSize: 25,
32191         titlebar: false,
32192         panels: [new CP("north", "North")]
32193     },
32194     west: {
32195         split:true,
32196         initialSize: 200,
32197         minSize: 175,
32198         maxSize: 400,
32199         titlebar: true,
32200         collapsible: true,
32201         panels: [new CP("west", {title: "West"})]
32202     },
32203     east: {
32204         split:true,
32205         initialSize: 202,
32206         minSize: 175,
32207         maxSize: 400,
32208         titlebar: true,
32209         collapsible: true,
32210         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
32211     },
32212     south: {
32213         split:true,
32214         initialSize: 100,
32215         minSize: 100,
32216         maxSize: 200,
32217         titlebar: true,
32218         collapsible: true,
32219         panels: [new CP("south", {title: "South", closable: true})]
32220     },
32221     center: {
32222         titlebar: true,
32223         autoScroll:true,
32224         resizeTabs: true,
32225         minTabWidth: 50,
32226         preferredTabWidth: 150,
32227         panels: [
32228             new CP("center1", {title: "Close Me", closable: true}),
32229             new CP("center2", {title: "Center Panel", closable: false})
32230         ]
32231     }
32232 }, document.body);
32233
32234 layout.getRegion("center").showPanel("center1");
32235 </code></pre>
32236  * @param config
32237  * @param targetEl
32238  */
32239 Roo.BorderLayout.create = function(config, targetEl){
32240     var layout = new Roo.BorderLayout(targetEl || document.body, config);
32241     layout.beginUpdate();
32242     var regions = Roo.BorderLayout.RegionFactory.validRegions;
32243     for(var j = 0, jlen = regions.length; j < jlen; j++){
32244         var lr = regions[j];
32245         if(layout.regions[lr] && config[lr].panels){
32246             var r = layout.regions[lr];
32247             var ps = config[lr].panels;
32248             layout.addTypedPanels(r, ps);
32249         }
32250     }
32251     layout.endUpdate();
32252     return layout;
32253 };
32254
32255 // private
32256 Roo.BorderLayout.RegionFactory = {
32257     // private
32258     validRegions : ["north","south","east","west","center"],
32259
32260     // private
32261     create : function(target, mgr, config){
32262         target = target.toLowerCase();
32263         if(config.lightweight || config.basic){
32264             return new Roo.BasicLayoutRegion(mgr, config, target);
32265         }
32266         switch(target){
32267             case "north":
32268                 return new Roo.NorthLayoutRegion(mgr, config);
32269             case "south":
32270                 return new Roo.SouthLayoutRegion(mgr, config);
32271             case "east":
32272                 return new Roo.EastLayoutRegion(mgr, config);
32273             case "west":
32274                 return new Roo.WestLayoutRegion(mgr, config);
32275             case "center":
32276                 return new Roo.CenterLayoutRegion(mgr, config);
32277         }
32278         throw 'Layout region "'+target+'" not supported.';
32279     }
32280 };/*
32281  * Based on:
32282  * Ext JS Library 1.1.1
32283  * Copyright(c) 2006-2007, Ext JS, LLC.
32284  *
32285  * Originally Released Under LGPL - original licence link has changed is not relivant.
32286  *
32287  * Fork - LGPL
32288  * <script type="text/javascript">
32289  */
32290  
32291 /**
32292  * @class Roo.BasicLayoutRegion
32293  * @extends Roo.util.Observable
32294  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
32295  * and does not have a titlebar, tabs or any other features. All it does is size and position 
32296  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
32297  */
32298 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
32299     this.mgr = mgr;
32300     this.position  = pos;
32301     this.events = {
32302         /**
32303          * @scope Roo.BasicLayoutRegion
32304          */
32305         
32306         /**
32307          * @event beforeremove
32308          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
32309          * @param {Roo.LayoutRegion} this
32310          * @param {Roo.ContentPanel} panel The panel
32311          * @param {Object} e The cancel event object
32312          */
32313         "beforeremove" : true,
32314         /**
32315          * @event invalidated
32316          * Fires when the layout for this region is changed.
32317          * @param {Roo.LayoutRegion} this
32318          */
32319         "invalidated" : true,
32320         /**
32321          * @event visibilitychange
32322          * Fires when this region is shown or hidden 
32323          * @param {Roo.LayoutRegion} this
32324          * @param {Boolean} visibility true or false
32325          */
32326         "visibilitychange" : true,
32327         /**
32328          * @event paneladded
32329          * Fires when a panel is added. 
32330          * @param {Roo.LayoutRegion} this
32331          * @param {Roo.ContentPanel} panel The panel
32332          */
32333         "paneladded" : true,
32334         /**
32335          * @event panelremoved
32336          * Fires when a panel is removed. 
32337          * @param {Roo.LayoutRegion} this
32338          * @param {Roo.ContentPanel} panel The panel
32339          */
32340         "panelremoved" : true,
32341         /**
32342          * @event collapsed
32343          * Fires when this region is collapsed.
32344          * @param {Roo.LayoutRegion} this
32345          */
32346         "collapsed" : true,
32347         /**
32348          * @event expanded
32349          * Fires when this region is expanded.
32350          * @param {Roo.LayoutRegion} this
32351          */
32352         "expanded" : true,
32353         /**
32354          * @event slideshow
32355          * Fires when this region is slid into view.
32356          * @param {Roo.LayoutRegion} this
32357          */
32358         "slideshow" : true,
32359         /**
32360          * @event slidehide
32361          * Fires when this region slides out of view. 
32362          * @param {Roo.LayoutRegion} this
32363          */
32364         "slidehide" : true,
32365         /**
32366          * @event panelactivated
32367          * Fires when a panel is activated. 
32368          * @param {Roo.LayoutRegion} this
32369          * @param {Roo.ContentPanel} panel The activated panel
32370          */
32371         "panelactivated" : true,
32372         /**
32373          * @event resized
32374          * Fires when the user resizes this region. 
32375          * @param {Roo.LayoutRegion} this
32376          * @param {Number} newSize The new size (width for east/west, height for north/south)
32377          */
32378         "resized" : true
32379     };
32380     /** A collection of panels in this region. @type Roo.util.MixedCollection */
32381     this.panels = new Roo.util.MixedCollection();
32382     this.panels.getKey = this.getPanelId.createDelegate(this);
32383     this.box = null;
32384     this.activePanel = null;
32385     // ensure listeners are added...
32386     
32387     if (config.listeners || config.events) {
32388         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
32389             listeners : config.listeners || {},
32390             events : config.events || {}
32391         });
32392     }
32393     
32394     if(skipConfig !== true){
32395         this.applyConfig(config);
32396     }
32397 };
32398
32399 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
32400     getPanelId : function(p){
32401         return p.getId();
32402     },
32403     
32404     applyConfig : function(config){
32405         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
32406         this.config = config;
32407         
32408     },
32409     
32410     /**
32411      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
32412      * the width, for horizontal (north, south) the height.
32413      * @param {Number} newSize The new width or height
32414      */
32415     resizeTo : function(newSize){
32416         var el = this.el ? this.el :
32417                  (this.activePanel ? this.activePanel.getEl() : null);
32418         if(el){
32419             switch(this.position){
32420                 case "east":
32421                 case "west":
32422                     el.setWidth(newSize);
32423                     this.fireEvent("resized", this, newSize);
32424                 break;
32425                 case "north":
32426                 case "south":
32427                     el.setHeight(newSize);
32428                     this.fireEvent("resized", this, newSize);
32429                 break;                
32430             }
32431         }
32432     },
32433     
32434     getBox : function(){
32435         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
32436     },
32437     
32438     getMargins : function(){
32439         return this.margins;
32440     },
32441     
32442     updateBox : function(box){
32443         this.box = box;
32444         var el = this.activePanel.getEl();
32445         el.dom.style.left = box.x + "px";
32446         el.dom.style.top = box.y + "px";
32447         this.activePanel.setSize(box.width, box.height);
32448     },
32449     
32450     /**
32451      * Returns the container element for this region.
32452      * @return {Roo.Element}
32453      */
32454     getEl : function(){
32455         return this.activePanel;
32456     },
32457     
32458     /**
32459      * Returns true if this region is currently visible.
32460      * @return {Boolean}
32461      */
32462     isVisible : function(){
32463         return this.activePanel ? true : false;
32464     },
32465     
32466     setActivePanel : function(panel){
32467         panel = this.getPanel(panel);
32468         if(this.activePanel && this.activePanel != panel){
32469             this.activePanel.setActiveState(false);
32470             this.activePanel.getEl().setLeftTop(-10000,-10000);
32471         }
32472         this.activePanel = panel;
32473         panel.setActiveState(true);
32474         if(this.box){
32475             panel.setSize(this.box.width, this.box.height);
32476         }
32477         this.fireEvent("panelactivated", this, panel);
32478         this.fireEvent("invalidated");
32479     },
32480     
32481     /**
32482      * Show the specified panel.
32483      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
32484      * @return {Roo.ContentPanel} The shown panel or null
32485      */
32486     showPanel : function(panel){
32487         if(panel = this.getPanel(panel)){
32488             this.setActivePanel(panel);
32489         }
32490         return panel;
32491     },
32492     
32493     /**
32494      * Get the active panel for this region.
32495      * @return {Roo.ContentPanel} The active panel or null
32496      */
32497     getActivePanel : function(){
32498         return this.activePanel;
32499     },
32500     
32501     /**
32502      * Add the passed ContentPanel(s)
32503      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32504      * @return {Roo.ContentPanel} The panel added (if only one was added)
32505      */
32506     add : function(panel){
32507         if(arguments.length > 1){
32508             for(var i = 0, len = arguments.length; i < len; i++) {
32509                 this.add(arguments[i]);
32510             }
32511             return null;
32512         }
32513         if(this.hasPanel(panel)){
32514             this.showPanel(panel);
32515             return panel;
32516         }
32517         var el = panel.getEl();
32518         if(el.dom.parentNode != this.mgr.el.dom){
32519             this.mgr.el.dom.appendChild(el.dom);
32520         }
32521         if(panel.setRegion){
32522             panel.setRegion(this);
32523         }
32524         this.panels.add(panel);
32525         el.setStyle("position", "absolute");
32526         if(!panel.background){
32527             this.setActivePanel(panel);
32528             if(this.config.initialSize && this.panels.getCount()==1){
32529                 this.resizeTo(this.config.initialSize);
32530             }
32531         }
32532         this.fireEvent("paneladded", this, panel);
32533         return panel;
32534     },
32535     
32536     /**
32537      * Returns true if the panel is in this region.
32538      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32539      * @return {Boolean}
32540      */
32541     hasPanel : function(panel){
32542         if(typeof panel == "object"){ // must be panel obj
32543             panel = panel.getId();
32544         }
32545         return this.getPanel(panel) ? true : false;
32546     },
32547     
32548     /**
32549      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
32550      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32551      * @param {Boolean} preservePanel Overrides the config preservePanel option
32552      * @return {Roo.ContentPanel} The panel that was removed
32553      */
32554     remove : function(panel, preservePanel){
32555         panel = this.getPanel(panel);
32556         if(!panel){
32557             return null;
32558         }
32559         var e = {};
32560         this.fireEvent("beforeremove", this, panel, e);
32561         if(e.cancel === true){
32562             return null;
32563         }
32564         var panelId = panel.getId();
32565         this.panels.removeKey(panelId);
32566         return panel;
32567     },
32568     
32569     /**
32570      * Returns the panel specified or null if it's not in this region.
32571      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32572      * @return {Roo.ContentPanel}
32573      */
32574     getPanel : function(id){
32575         if(typeof id == "object"){ // must be panel obj
32576             return id;
32577         }
32578         return this.panels.get(id);
32579     },
32580     
32581     /**
32582      * Returns this regions position (north/south/east/west/center).
32583      * @return {String} 
32584      */
32585     getPosition: function(){
32586         return this.position;    
32587     }
32588 });/*
32589  * Based on:
32590  * Ext JS Library 1.1.1
32591  * Copyright(c) 2006-2007, Ext JS, LLC.
32592  *
32593  * Originally Released Under LGPL - original licence link has changed is not relivant.
32594  *
32595  * Fork - LGPL
32596  * <script type="text/javascript">
32597  */
32598  
32599 /**
32600  * @class Roo.LayoutRegion
32601  * @extends Roo.BasicLayoutRegion
32602  * This class represents a region in a layout manager.
32603  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
32604  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
32605  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
32606  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
32607  * @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})
32608  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
32609  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
32610  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
32611  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
32612  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
32613  * @cfg {String}    title           The title for the region (overrides panel titles)
32614  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
32615  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
32616  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
32617  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
32618  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
32619  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
32620  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
32621  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
32622  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
32623  * @cfg {Boolean}   showPin         True to show a pin button
32624  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
32625  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
32626  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
32627  * @cfg {Number}    width           For East/West panels
32628  * @cfg {Number}    height          For North/South panels
32629  * @cfg {Boolean}   split           To show the splitter
32630  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
32631  */
32632 Roo.LayoutRegion = function(mgr, config, pos){
32633     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
32634     var dh = Roo.DomHelper;
32635     /** This region's container element 
32636     * @type Roo.Element */
32637     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
32638     /** This region's title element 
32639     * @type Roo.Element */
32640
32641     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
32642         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
32643         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
32644     ]}, true);
32645     this.titleEl.enableDisplayMode();
32646     /** This region's title text element 
32647     * @type HTMLElement */
32648     this.titleTextEl = this.titleEl.dom.firstChild;
32649     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
32650     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
32651     this.closeBtn.enableDisplayMode();
32652     this.closeBtn.on("click", this.closeClicked, this);
32653     this.closeBtn.hide();
32654
32655     this.createBody(config);
32656     this.visible = true;
32657     this.collapsed = false;
32658
32659     if(config.hideWhenEmpty){
32660         this.hide();
32661         this.on("paneladded", this.validateVisibility, this);
32662         this.on("panelremoved", this.validateVisibility, this);
32663     }
32664     this.applyConfig(config);
32665 };
32666
32667 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
32668
32669     createBody : function(){
32670         /** This region's body element 
32671         * @type Roo.Element */
32672         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
32673     },
32674
32675     applyConfig : function(c){
32676         if(c.collapsible && this.position != "center" && !this.collapsedEl){
32677             var dh = Roo.DomHelper;
32678             if(c.titlebar !== false){
32679                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
32680                 this.collapseBtn.on("click", this.collapse, this);
32681                 this.collapseBtn.enableDisplayMode();
32682
32683                 if(c.showPin === true || this.showPin){
32684                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
32685                     this.stickBtn.enableDisplayMode();
32686                     this.stickBtn.on("click", this.expand, this);
32687                     this.stickBtn.hide();
32688                 }
32689             }
32690             /** This region's collapsed element
32691             * @type Roo.Element */
32692             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
32693                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
32694             ]}, true);
32695             if(c.floatable !== false){
32696                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
32697                this.collapsedEl.on("click", this.collapseClick, this);
32698             }
32699
32700             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
32701                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
32702                    id: "message", unselectable: "on", style:{"float":"left"}});
32703                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
32704              }
32705             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
32706             this.expandBtn.on("click", this.expand, this);
32707         }
32708         if(this.collapseBtn){
32709             this.collapseBtn.setVisible(c.collapsible == true);
32710         }
32711         this.cmargins = c.cmargins || this.cmargins ||
32712                          (this.position == "west" || this.position == "east" ?
32713                              {top: 0, left: 2, right:2, bottom: 0} :
32714                              {top: 2, left: 0, right:0, bottom: 2});
32715         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
32716         this.bottomTabs = c.tabPosition != "top";
32717         this.autoScroll = c.autoScroll || false;
32718         if(this.autoScroll){
32719             this.bodyEl.setStyle("overflow", "auto");
32720         }else{
32721             this.bodyEl.setStyle("overflow", "hidden");
32722         }
32723         //if(c.titlebar !== false){
32724             if((!c.titlebar && !c.title) || c.titlebar === false){
32725                 this.titleEl.hide();
32726             }else{
32727                 this.titleEl.show();
32728                 if(c.title){
32729                     this.titleTextEl.innerHTML = c.title;
32730                 }
32731             }
32732         //}
32733         this.duration = c.duration || .30;
32734         this.slideDuration = c.slideDuration || .45;
32735         this.config = c;
32736         if(c.collapsed){
32737             this.collapse(true);
32738         }
32739         if(c.hidden){
32740             this.hide();
32741         }
32742     },
32743     /**
32744      * Returns true if this region is currently visible.
32745      * @return {Boolean}
32746      */
32747     isVisible : function(){
32748         return this.visible;
32749     },
32750
32751     /**
32752      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
32753      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
32754      */
32755     setCollapsedTitle : function(title){
32756         title = title || "&#160;";
32757         if(this.collapsedTitleTextEl){
32758             this.collapsedTitleTextEl.innerHTML = title;
32759         }
32760     },
32761
32762     getBox : function(){
32763         var b;
32764         if(!this.collapsed){
32765             b = this.el.getBox(false, true);
32766         }else{
32767             b = this.collapsedEl.getBox(false, true);
32768         }
32769         return b;
32770     },
32771
32772     getMargins : function(){
32773         return this.collapsed ? this.cmargins : this.margins;
32774     },
32775
32776     highlight : function(){
32777         this.el.addClass("x-layout-panel-dragover");
32778     },
32779
32780     unhighlight : function(){
32781         this.el.removeClass("x-layout-panel-dragover");
32782     },
32783
32784     updateBox : function(box){
32785         this.box = box;
32786         if(!this.collapsed){
32787             this.el.dom.style.left = box.x + "px";
32788             this.el.dom.style.top = box.y + "px";
32789             this.updateBody(box.width, box.height);
32790         }else{
32791             this.collapsedEl.dom.style.left = box.x + "px";
32792             this.collapsedEl.dom.style.top = box.y + "px";
32793             this.collapsedEl.setSize(box.width, box.height);
32794         }
32795         if(this.tabs){
32796             this.tabs.autoSizeTabs();
32797         }
32798     },
32799
32800     updateBody : function(w, h){
32801         if(w !== null){
32802             this.el.setWidth(w);
32803             w -= this.el.getBorderWidth("rl");
32804             if(this.config.adjustments){
32805                 w += this.config.adjustments[0];
32806             }
32807         }
32808         if(h !== null){
32809             this.el.setHeight(h);
32810             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
32811             h -= this.el.getBorderWidth("tb");
32812             if(this.config.adjustments){
32813                 h += this.config.adjustments[1];
32814             }
32815             this.bodyEl.setHeight(h);
32816             if(this.tabs){
32817                 h = this.tabs.syncHeight(h);
32818             }
32819         }
32820         if(this.panelSize){
32821             w = w !== null ? w : this.panelSize.width;
32822             h = h !== null ? h : this.panelSize.height;
32823         }
32824         if(this.activePanel){
32825             var el = this.activePanel.getEl();
32826             w = w !== null ? w : el.getWidth();
32827             h = h !== null ? h : el.getHeight();
32828             this.panelSize = {width: w, height: h};
32829             this.activePanel.setSize(w, h);
32830         }
32831         if(Roo.isIE && this.tabs){
32832             this.tabs.el.repaint();
32833         }
32834     },
32835
32836     /**
32837      * Returns the container element for this region.
32838      * @return {Roo.Element}
32839      */
32840     getEl : function(){
32841         return this.el;
32842     },
32843
32844     /**
32845      * Hides this region.
32846      */
32847     hide : function(){
32848         if(!this.collapsed){
32849             this.el.dom.style.left = "-2000px";
32850             this.el.hide();
32851         }else{
32852             this.collapsedEl.dom.style.left = "-2000px";
32853             this.collapsedEl.hide();
32854         }
32855         this.visible = false;
32856         this.fireEvent("visibilitychange", this, false);
32857     },
32858
32859     /**
32860      * Shows this region if it was previously hidden.
32861      */
32862     show : function(){
32863         if(!this.collapsed){
32864             this.el.show();
32865         }else{
32866             this.collapsedEl.show();
32867         }
32868         this.visible = true;
32869         this.fireEvent("visibilitychange", this, true);
32870     },
32871
32872     closeClicked : function(){
32873         if(this.activePanel){
32874             this.remove(this.activePanel);
32875         }
32876     },
32877
32878     collapseClick : function(e){
32879         if(this.isSlid){
32880            e.stopPropagation();
32881            this.slideIn();
32882         }else{
32883            e.stopPropagation();
32884            this.slideOut();
32885         }
32886     },
32887
32888     /**
32889      * Collapses this region.
32890      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
32891      */
32892     collapse : function(skipAnim){
32893         if(this.collapsed) return;
32894         this.collapsed = true;
32895         if(this.split){
32896             this.split.el.hide();
32897         }
32898         if(this.config.animate && skipAnim !== true){
32899             this.fireEvent("invalidated", this);
32900             this.animateCollapse();
32901         }else{
32902             this.el.setLocation(-20000,-20000);
32903             this.el.hide();
32904             this.collapsedEl.show();
32905             this.fireEvent("collapsed", this);
32906             this.fireEvent("invalidated", this);
32907         }
32908     },
32909
32910     animateCollapse : function(){
32911         // overridden
32912     },
32913
32914     /**
32915      * Expands this region if it was previously collapsed.
32916      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
32917      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
32918      */
32919     expand : function(e, skipAnim){
32920         if(e) e.stopPropagation();
32921         if(!this.collapsed || this.el.hasActiveFx()) return;
32922         if(this.isSlid){
32923             this.afterSlideIn();
32924             skipAnim = true;
32925         }
32926         this.collapsed = false;
32927         if(this.config.animate && skipAnim !== true){
32928             this.animateExpand();
32929         }else{
32930             this.el.show();
32931             if(this.split){
32932                 this.split.el.show();
32933             }
32934             this.collapsedEl.setLocation(-2000,-2000);
32935             this.collapsedEl.hide();
32936             this.fireEvent("invalidated", this);
32937             this.fireEvent("expanded", this);
32938         }
32939     },
32940
32941     animateExpand : function(){
32942         // overridden
32943     },
32944
32945     initTabs : function()
32946     {
32947         this.bodyEl.setStyle("overflow", "hidden");
32948         var ts = new Roo.TabPanel(
32949                 this.bodyEl.dom,
32950                 {
32951                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
32952                     disableTooltips: this.config.disableTabTips,
32953                     toolbar : this.config.toolbar
32954                 }
32955         );
32956         if(this.config.hideTabs){
32957             ts.stripWrap.setDisplayed(false);
32958         }
32959         this.tabs = ts;
32960         ts.resizeTabs = this.config.resizeTabs === true;
32961         ts.minTabWidth = this.config.minTabWidth || 40;
32962         ts.maxTabWidth = this.config.maxTabWidth || 250;
32963         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
32964         ts.monitorResize = false;
32965         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
32966         ts.bodyEl.addClass('x-layout-tabs-body');
32967         this.panels.each(this.initPanelAsTab, this);
32968     },
32969
32970     initPanelAsTab : function(panel){
32971         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
32972                     this.config.closeOnTab && panel.isClosable());
32973         if(panel.tabTip !== undefined){
32974             ti.setTooltip(panel.tabTip);
32975         }
32976         ti.on("activate", function(){
32977               this.setActivePanel(panel);
32978         }, this);
32979         if(this.config.closeOnTab){
32980             ti.on("beforeclose", function(t, e){
32981                 e.cancel = true;
32982                 this.remove(panel);
32983             }, this);
32984         }
32985         return ti;
32986     },
32987
32988     updatePanelTitle : function(panel, title){
32989         if(this.activePanel == panel){
32990             this.updateTitle(title);
32991         }
32992         if(this.tabs){
32993             var ti = this.tabs.getTab(panel.getEl().id);
32994             ti.setText(title);
32995             if(panel.tabTip !== undefined){
32996                 ti.setTooltip(panel.tabTip);
32997             }
32998         }
32999     },
33000
33001     updateTitle : function(title){
33002         if(this.titleTextEl && !this.config.title){
33003             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
33004         }
33005     },
33006
33007     setActivePanel : function(panel){
33008         panel = this.getPanel(panel);
33009         if(this.activePanel && this.activePanel != panel){
33010             this.activePanel.setActiveState(false);
33011         }
33012         this.activePanel = panel;
33013         panel.setActiveState(true);
33014         if(this.panelSize){
33015             panel.setSize(this.panelSize.width, this.panelSize.height);
33016         }
33017         if(this.closeBtn){
33018             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
33019         }
33020         this.updateTitle(panel.getTitle());
33021         if(this.tabs){
33022             this.fireEvent("invalidated", this);
33023         }
33024         this.fireEvent("panelactivated", this, panel);
33025     },
33026
33027     /**
33028      * Shows the specified panel.
33029      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
33030      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
33031      */
33032     showPanel : function(panel){
33033         if(panel = this.getPanel(panel)){
33034             if(this.tabs){
33035                 var tab = this.tabs.getTab(panel.getEl().id);
33036                 if(tab.isHidden()){
33037                     this.tabs.unhideTab(tab.id);
33038                 }
33039                 tab.activate();
33040             }else{
33041                 this.setActivePanel(panel);
33042             }
33043         }
33044         return panel;
33045     },
33046
33047     /**
33048      * Get the active panel for this region.
33049      * @return {Roo.ContentPanel} The active panel or null
33050      */
33051     getActivePanel : function(){
33052         return this.activePanel;
33053     },
33054
33055     validateVisibility : function(){
33056         if(this.panels.getCount() < 1){
33057             this.updateTitle("&#160;");
33058             this.closeBtn.hide();
33059             this.hide();
33060         }else{
33061             if(!this.isVisible()){
33062                 this.show();
33063             }
33064         }
33065     },
33066
33067     /**
33068      * Adds the passed ContentPanel(s) to this region.
33069      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33070      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
33071      */
33072     add : function(panel){
33073         if(arguments.length > 1){
33074             for(var i = 0, len = arguments.length; i < len; i++) {
33075                 this.add(arguments[i]);
33076             }
33077             return null;
33078         }
33079         if(this.hasPanel(panel)){
33080             this.showPanel(panel);
33081             return panel;
33082         }
33083         panel.setRegion(this);
33084         this.panels.add(panel);
33085         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
33086             this.bodyEl.dom.appendChild(panel.getEl().dom);
33087             if(panel.background !== true){
33088                 this.setActivePanel(panel);
33089             }
33090             this.fireEvent("paneladded", this, panel);
33091             return panel;
33092         }
33093         if(!this.tabs){
33094             this.initTabs();
33095         }else{
33096             this.initPanelAsTab(panel);
33097         }
33098         if(panel.background !== true){
33099             this.tabs.activate(panel.getEl().id);
33100         }
33101         this.fireEvent("paneladded", this, panel);
33102         return panel;
33103     },
33104
33105     /**
33106      * Hides the tab for the specified panel.
33107      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33108      */
33109     hidePanel : function(panel){
33110         if(this.tabs && (panel = this.getPanel(panel))){
33111             this.tabs.hideTab(panel.getEl().id);
33112         }
33113     },
33114
33115     /**
33116      * Unhides the tab for a previously hidden panel.
33117      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33118      */
33119     unhidePanel : function(panel){
33120         if(this.tabs && (panel = this.getPanel(panel))){
33121             this.tabs.unhideTab(panel.getEl().id);
33122         }
33123     },
33124
33125     clearPanels : function(){
33126         while(this.panels.getCount() > 0){
33127              this.remove(this.panels.first());
33128         }
33129     },
33130
33131     /**
33132      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33133      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33134      * @param {Boolean} preservePanel Overrides the config preservePanel option
33135      * @return {Roo.ContentPanel} The panel that was removed
33136      */
33137     remove : function(panel, preservePanel){
33138         panel = this.getPanel(panel);
33139         if(!panel){
33140             return null;
33141         }
33142         var e = {};
33143         this.fireEvent("beforeremove", this, panel, e);
33144         if(e.cancel === true){
33145             return null;
33146         }
33147         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
33148         var panelId = panel.getId();
33149         this.panels.removeKey(panelId);
33150         if(preservePanel){
33151             document.body.appendChild(panel.getEl().dom);
33152         }
33153         if(this.tabs){
33154             this.tabs.removeTab(panel.getEl().id);
33155         }else if (!preservePanel){
33156             this.bodyEl.dom.removeChild(panel.getEl().dom);
33157         }
33158         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
33159             var p = this.panels.first();
33160             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
33161             tempEl.appendChild(p.getEl().dom);
33162             this.bodyEl.update("");
33163             this.bodyEl.dom.appendChild(p.getEl().dom);
33164             tempEl = null;
33165             this.updateTitle(p.getTitle());
33166             this.tabs = null;
33167             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
33168             this.setActivePanel(p);
33169         }
33170         panel.setRegion(null);
33171         if(this.activePanel == panel){
33172             this.activePanel = null;
33173         }
33174         if(this.config.autoDestroy !== false && preservePanel !== true){
33175             try{panel.destroy();}catch(e){}
33176         }
33177         this.fireEvent("panelremoved", this, panel);
33178         return panel;
33179     },
33180
33181     /**
33182      * Returns the TabPanel component used by this region
33183      * @return {Roo.TabPanel}
33184      */
33185     getTabs : function(){
33186         return this.tabs;
33187     },
33188
33189     createTool : function(parentEl, className){
33190         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
33191             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
33192         btn.addClassOnOver("x-layout-tools-button-over");
33193         return btn;
33194     }
33195 });/*
33196  * Based on:
33197  * Ext JS Library 1.1.1
33198  * Copyright(c) 2006-2007, Ext JS, LLC.
33199  *
33200  * Originally Released Under LGPL - original licence link has changed is not relivant.
33201  *
33202  * Fork - LGPL
33203  * <script type="text/javascript">
33204  */
33205  
33206
33207
33208 /**
33209  * @class Roo.SplitLayoutRegion
33210  * @extends Roo.LayoutRegion
33211  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
33212  */
33213 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
33214     this.cursor = cursor;
33215     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
33216 };
33217
33218 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
33219     splitTip : "Drag to resize.",
33220     collapsibleSplitTip : "Drag to resize. Double click to hide.",
33221     useSplitTips : false,
33222
33223     applyConfig : function(config){
33224         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
33225         if(config.split){
33226             if(!this.split){
33227                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
33228                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
33229                 /** The SplitBar for this region 
33230                 * @type Roo.SplitBar */
33231                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
33232                 this.split.on("moved", this.onSplitMove, this);
33233                 this.split.useShim = config.useShim === true;
33234                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
33235                 if(this.useSplitTips){
33236                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
33237                 }
33238                 if(config.collapsible){
33239                     this.split.el.on("dblclick", this.collapse,  this);
33240                 }
33241             }
33242             if(typeof config.minSize != "undefined"){
33243                 this.split.minSize = config.minSize;
33244             }
33245             if(typeof config.maxSize != "undefined"){
33246                 this.split.maxSize = config.maxSize;
33247             }
33248             if(config.hideWhenEmpty || config.hidden || config.collapsed){
33249                 this.hideSplitter();
33250             }
33251         }
33252     },
33253
33254     getHMaxSize : function(){
33255          var cmax = this.config.maxSize || 10000;
33256          var center = this.mgr.getRegion("center");
33257          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
33258     },
33259
33260     getVMaxSize : function(){
33261          var cmax = this.config.maxSize || 10000;
33262          var center = this.mgr.getRegion("center");
33263          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
33264     },
33265
33266     onSplitMove : function(split, newSize){
33267         this.fireEvent("resized", this, newSize);
33268     },
33269     
33270     /** 
33271      * Returns the {@link Roo.SplitBar} for this region.
33272      * @return {Roo.SplitBar}
33273      */
33274     getSplitBar : function(){
33275         return this.split;
33276     },
33277     
33278     hide : function(){
33279         this.hideSplitter();
33280         Roo.SplitLayoutRegion.superclass.hide.call(this);
33281     },
33282
33283     hideSplitter : function(){
33284         if(this.split){
33285             this.split.el.setLocation(-2000,-2000);
33286             this.split.el.hide();
33287         }
33288     },
33289
33290     show : function(){
33291         if(this.split){
33292             this.split.el.show();
33293         }
33294         Roo.SplitLayoutRegion.superclass.show.call(this);
33295     },
33296     
33297     beforeSlide: function(){
33298         if(Roo.isGecko){// firefox overflow auto bug workaround
33299             this.bodyEl.clip();
33300             if(this.tabs) this.tabs.bodyEl.clip();
33301             if(this.activePanel){
33302                 this.activePanel.getEl().clip();
33303                 
33304                 if(this.activePanel.beforeSlide){
33305                     this.activePanel.beforeSlide();
33306                 }
33307             }
33308         }
33309     },
33310     
33311     afterSlide : function(){
33312         if(Roo.isGecko){// firefox overflow auto bug workaround
33313             this.bodyEl.unclip();
33314             if(this.tabs) this.tabs.bodyEl.unclip();
33315             if(this.activePanel){
33316                 this.activePanel.getEl().unclip();
33317                 if(this.activePanel.afterSlide){
33318                     this.activePanel.afterSlide();
33319                 }
33320             }
33321         }
33322     },
33323
33324     initAutoHide : function(){
33325         if(this.autoHide !== false){
33326             if(!this.autoHideHd){
33327                 var st = new Roo.util.DelayedTask(this.slideIn, this);
33328                 this.autoHideHd = {
33329                     "mouseout": function(e){
33330                         if(!e.within(this.el, true)){
33331                             st.delay(500);
33332                         }
33333                     },
33334                     "mouseover" : function(e){
33335                         st.cancel();
33336                     },
33337                     scope : this
33338                 };
33339             }
33340             this.el.on(this.autoHideHd);
33341         }
33342     },
33343
33344     clearAutoHide : function(){
33345         if(this.autoHide !== false){
33346             this.el.un("mouseout", this.autoHideHd.mouseout);
33347             this.el.un("mouseover", this.autoHideHd.mouseover);
33348         }
33349     },
33350
33351     clearMonitor : function(){
33352         Roo.get(document).un("click", this.slideInIf, this);
33353     },
33354
33355     // these names are backwards but not changed for compat
33356     slideOut : function(){
33357         if(this.isSlid || this.el.hasActiveFx()){
33358             return;
33359         }
33360         this.isSlid = true;
33361         if(this.collapseBtn){
33362             this.collapseBtn.hide();
33363         }
33364         this.closeBtnState = this.closeBtn.getStyle('display');
33365         this.closeBtn.hide();
33366         if(this.stickBtn){
33367             this.stickBtn.show();
33368         }
33369         this.el.show();
33370         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
33371         this.beforeSlide();
33372         this.el.setStyle("z-index", 10001);
33373         this.el.slideIn(this.getSlideAnchor(), {
33374             callback: function(){
33375                 this.afterSlide();
33376                 this.initAutoHide();
33377                 Roo.get(document).on("click", this.slideInIf, this);
33378                 this.fireEvent("slideshow", this);
33379             },
33380             scope: this,
33381             block: true
33382         });
33383     },
33384
33385     afterSlideIn : function(){
33386         this.clearAutoHide();
33387         this.isSlid = false;
33388         this.clearMonitor();
33389         this.el.setStyle("z-index", "");
33390         if(this.collapseBtn){
33391             this.collapseBtn.show();
33392         }
33393         this.closeBtn.setStyle('display', this.closeBtnState);
33394         if(this.stickBtn){
33395             this.stickBtn.hide();
33396         }
33397         this.fireEvent("slidehide", this);
33398     },
33399
33400     slideIn : function(cb){
33401         if(!this.isSlid || this.el.hasActiveFx()){
33402             Roo.callback(cb);
33403             return;
33404         }
33405         this.isSlid = false;
33406         this.beforeSlide();
33407         this.el.slideOut(this.getSlideAnchor(), {
33408             callback: function(){
33409                 this.el.setLeftTop(-10000, -10000);
33410                 this.afterSlide();
33411                 this.afterSlideIn();
33412                 Roo.callback(cb);
33413             },
33414             scope: this,
33415             block: true
33416         });
33417     },
33418     
33419     slideInIf : function(e){
33420         if(!e.within(this.el)){
33421             this.slideIn();
33422         }
33423     },
33424
33425     animateCollapse : function(){
33426         this.beforeSlide();
33427         this.el.setStyle("z-index", 20000);
33428         var anchor = this.getSlideAnchor();
33429         this.el.slideOut(anchor, {
33430             callback : function(){
33431                 this.el.setStyle("z-index", "");
33432                 this.collapsedEl.slideIn(anchor, {duration:.3});
33433                 this.afterSlide();
33434                 this.el.setLocation(-10000,-10000);
33435                 this.el.hide();
33436                 this.fireEvent("collapsed", this);
33437             },
33438             scope: this,
33439             block: true
33440         });
33441     },
33442
33443     animateExpand : function(){
33444         this.beforeSlide();
33445         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
33446         this.el.setStyle("z-index", 20000);
33447         this.collapsedEl.hide({
33448             duration:.1
33449         });
33450         this.el.slideIn(this.getSlideAnchor(), {
33451             callback : function(){
33452                 this.el.setStyle("z-index", "");
33453                 this.afterSlide();
33454                 if(this.split){
33455                     this.split.el.show();
33456                 }
33457                 this.fireEvent("invalidated", this);
33458                 this.fireEvent("expanded", this);
33459             },
33460             scope: this,
33461             block: true
33462         });
33463     },
33464
33465     anchors : {
33466         "west" : "left",
33467         "east" : "right",
33468         "north" : "top",
33469         "south" : "bottom"
33470     },
33471
33472     sanchors : {
33473         "west" : "l",
33474         "east" : "r",
33475         "north" : "t",
33476         "south" : "b"
33477     },
33478
33479     canchors : {
33480         "west" : "tl-tr",
33481         "east" : "tr-tl",
33482         "north" : "tl-bl",
33483         "south" : "bl-tl"
33484     },
33485
33486     getAnchor : function(){
33487         return this.anchors[this.position];
33488     },
33489
33490     getCollapseAnchor : function(){
33491         return this.canchors[this.position];
33492     },
33493
33494     getSlideAnchor : function(){
33495         return this.sanchors[this.position];
33496     },
33497
33498     getAlignAdj : function(){
33499         var cm = this.cmargins;
33500         switch(this.position){
33501             case "west":
33502                 return [0, 0];
33503             break;
33504             case "east":
33505                 return [0, 0];
33506             break;
33507             case "north":
33508                 return [0, 0];
33509             break;
33510             case "south":
33511                 return [0, 0];
33512             break;
33513         }
33514     },
33515
33516     getExpandAdj : function(){
33517         var c = this.collapsedEl, cm = this.cmargins;
33518         switch(this.position){
33519             case "west":
33520                 return [-(cm.right+c.getWidth()+cm.left), 0];
33521             break;
33522             case "east":
33523                 return [cm.right+c.getWidth()+cm.left, 0];
33524             break;
33525             case "north":
33526                 return [0, -(cm.top+cm.bottom+c.getHeight())];
33527             break;
33528             case "south":
33529                 return [0, cm.top+cm.bottom+c.getHeight()];
33530             break;
33531         }
33532     }
33533 });/*
33534  * Based on:
33535  * Ext JS Library 1.1.1
33536  * Copyright(c) 2006-2007, Ext JS, LLC.
33537  *
33538  * Originally Released Under LGPL - original licence link has changed is not relivant.
33539  *
33540  * Fork - LGPL
33541  * <script type="text/javascript">
33542  */
33543 /*
33544  * These classes are private internal classes
33545  */
33546 Roo.CenterLayoutRegion = function(mgr, config){
33547     Roo.LayoutRegion.call(this, mgr, config, "center");
33548     this.visible = true;
33549     this.minWidth = config.minWidth || 20;
33550     this.minHeight = config.minHeight || 20;
33551 };
33552
33553 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
33554     hide : function(){
33555         // center panel can't be hidden
33556     },
33557     
33558     show : function(){
33559         // center panel can't be hidden
33560     },
33561     
33562     getMinWidth: function(){
33563         return this.minWidth;
33564     },
33565     
33566     getMinHeight: function(){
33567         return this.minHeight;
33568     }
33569 });
33570
33571
33572 Roo.NorthLayoutRegion = function(mgr, config){
33573     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
33574     if(this.split){
33575         this.split.placement = Roo.SplitBar.TOP;
33576         this.split.orientation = Roo.SplitBar.VERTICAL;
33577         this.split.el.addClass("x-layout-split-v");
33578     }
33579     var size = config.initialSize || config.height;
33580     if(typeof size != "undefined"){
33581         this.el.setHeight(size);
33582     }
33583 };
33584 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
33585     orientation: Roo.SplitBar.VERTICAL,
33586     getBox : function(){
33587         if(this.collapsed){
33588             return this.collapsedEl.getBox();
33589         }
33590         var box = this.el.getBox();
33591         if(this.split){
33592             box.height += this.split.el.getHeight();
33593         }
33594         return box;
33595     },
33596     
33597     updateBox : function(box){
33598         if(this.split && !this.collapsed){
33599             box.height -= this.split.el.getHeight();
33600             this.split.el.setLeft(box.x);
33601             this.split.el.setTop(box.y+box.height);
33602             this.split.el.setWidth(box.width);
33603         }
33604         if(this.collapsed){
33605             this.updateBody(box.width, null);
33606         }
33607         Roo.LayoutRegion.prototype.updateBox.call(this, box);
33608     }
33609 });
33610
33611 Roo.SouthLayoutRegion = function(mgr, config){
33612     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
33613     if(this.split){
33614         this.split.placement = Roo.SplitBar.BOTTOM;
33615         this.split.orientation = Roo.SplitBar.VERTICAL;
33616         this.split.el.addClass("x-layout-split-v");
33617     }
33618     var size = config.initialSize || config.height;
33619     if(typeof size != "undefined"){
33620         this.el.setHeight(size);
33621     }
33622 };
33623 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
33624     orientation: Roo.SplitBar.VERTICAL,
33625     getBox : function(){
33626         if(this.collapsed){
33627             return this.collapsedEl.getBox();
33628         }
33629         var box = this.el.getBox();
33630         if(this.split){
33631             var sh = this.split.el.getHeight();
33632             box.height += sh;
33633             box.y -= sh;
33634         }
33635         return box;
33636     },
33637     
33638     updateBox : function(box){
33639         if(this.split && !this.collapsed){
33640             var sh = this.split.el.getHeight();
33641             box.height -= sh;
33642             box.y += sh;
33643             this.split.el.setLeft(box.x);
33644             this.split.el.setTop(box.y-sh);
33645             this.split.el.setWidth(box.width);
33646         }
33647         if(this.collapsed){
33648             this.updateBody(box.width, null);
33649         }
33650         Roo.LayoutRegion.prototype.updateBox.call(this, box);
33651     }
33652 });
33653
33654 Roo.EastLayoutRegion = function(mgr, config){
33655     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
33656     if(this.split){
33657         this.split.placement = Roo.SplitBar.RIGHT;
33658         this.split.orientation = Roo.SplitBar.HORIZONTAL;
33659         this.split.el.addClass("x-layout-split-h");
33660     }
33661     var size = config.initialSize || config.width;
33662     if(typeof size != "undefined"){
33663         this.el.setWidth(size);
33664     }
33665 };
33666 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
33667     orientation: Roo.SplitBar.HORIZONTAL,
33668     getBox : function(){
33669         if(this.collapsed){
33670             return this.collapsedEl.getBox();
33671         }
33672         var box = this.el.getBox();
33673         if(this.split){
33674             var sw = this.split.el.getWidth();
33675             box.width += sw;
33676             box.x -= sw;
33677         }
33678         return box;
33679     },
33680
33681     updateBox : function(box){
33682         if(this.split && !this.collapsed){
33683             var sw = this.split.el.getWidth();
33684             box.width -= sw;
33685             this.split.el.setLeft(box.x);
33686             this.split.el.setTop(box.y);
33687             this.split.el.setHeight(box.height);
33688             box.x += sw;
33689         }
33690         if(this.collapsed){
33691             this.updateBody(null, box.height);
33692         }
33693         Roo.LayoutRegion.prototype.updateBox.call(this, box);
33694     }
33695 });
33696
33697 Roo.WestLayoutRegion = function(mgr, config){
33698     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
33699     if(this.split){
33700         this.split.placement = Roo.SplitBar.LEFT;
33701         this.split.orientation = Roo.SplitBar.HORIZONTAL;
33702         this.split.el.addClass("x-layout-split-h");
33703     }
33704     var size = config.initialSize || config.width;
33705     if(typeof size != "undefined"){
33706         this.el.setWidth(size);
33707     }
33708 };
33709 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
33710     orientation: Roo.SplitBar.HORIZONTAL,
33711     getBox : function(){
33712         if(this.collapsed){
33713             return this.collapsedEl.getBox();
33714         }
33715         var box = this.el.getBox();
33716         if(this.split){
33717             box.width += this.split.el.getWidth();
33718         }
33719         return box;
33720     },
33721     
33722     updateBox : function(box){
33723         if(this.split && !this.collapsed){
33724             var sw = this.split.el.getWidth();
33725             box.width -= sw;
33726             this.split.el.setLeft(box.x+box.width);
33727             this.split.el.setTop(box.y);
33728             this.split.el.setHeight(box.height);
33729         }
33730         if(this.collapsed){
33731             this.updateBody(null, box.height);
33732         }
33733         Roo.LayoutRegion.prototype.updateBox.call(this, box);
33734     }
33735 });
33736 /*
33737  * Based on:
33738  * Ext JS Library 1.1.1
33739  * Copyright(c) 2006-2007, Ext JS, LLC.
33740  *
33741  * Originally Released Under LGPL - original licence link has changed is not relivant.
33742  *
33743  * Fork - LGPL
33744  * <script type="text/javascript">
33745  */
33746  
33747  
33748 /*
33749  * Private internal class for reading and applying state
33750  */
33751 Roo.LayoutStateManager = function(layout){
33752      // default empty state
33753      this.state = {
33754         north: {},
33755         south: {},
33756         east: {},
33757         west: {}       
33758     };
33759 };
33760
33761 Roo.LayoutStateManager.prototype = {
33762     init : function(layout, provider){
33763         this.provider = provider;
33764         var state = provider.get(layout.id+"-layout-state");
33765         if(state){
33766             var wasUpdating = layout.isUpdating();
33767             if(!wasUpdating){
33768                 layout.beginUpdate();
33769             }
33770             for(var key in state){
33771                 if(typeof state[key] != "function"){
33772                     var rstate = state[key];
33773                     var r = layout.getRegion(key);
33774                     if(r && rstate){
33775                         if(rstate.size){
33776                             r.resizeTo(rstate.size);
33777                         }
33778                         if(rstate.collapsed == true){
33779                             r.collapse(true);
33780                         }else{
33781                             r.expand(null, true);
33782                         }
33783                     }
33784                 }
33785             }
33786             if(!wasUpdating){
33787                 layout.endUpdate();
33788             }
33789             this.state = state; 
33790         }
33791         this.layout = layout;
33792         layout.on("regionresized", this.onRegionResized, this);
33793         layout.on("regioncollapsed", this.onRegionCollapsed, this);
33794         layout.on("regionexpanded", this.onRegionExpanded, this);
33795     },
33796     
33797     storeState : function(){
33798         this.provider.set(this.layout.id+"-layout-state", this.state);
33799     },
33800     
33801     onRegionResized : function(region, newSize){
33802         this.state[region.getPosition()].size = newSize;
33803         this.storeState();
33804     },
33805     
33806     onRegionCollapsed : function(region){
33807         this.state[region.getPosition()].collapsed = true;
33808         this.storeState();
33809     },
33810     
33811     onRegionExpanded : function(region){
33812         this.state[region.getPosition()].collapsed = false;
33813         this.storeState();
33814     }
33815 };/*
33816  * Based on:
33817  * Ext JS Library 1.1.1
33818  * Copyright(c) 2006-2007, Ext JS, LLC.
33819  *
33820  * Originally Released Under LGPL - original licence link has changed is not relivant.
33821  *
33822  * Fork - LGPL
33823  * <script type="text/javascript">
33824  */
33825 /**
33826  * @class Roo.ContentPanel
33827  * @extends Roo.util.Observable
33828  * A basic ContentPanel element.
33829  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
33830  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
33831  * @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
33832  * @cfg {Boolean}   closable      True if the panel can be closed/removed
33833  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
33834  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
33835  * @cfg {Toolbar}   toolbar       A toolbar for this panel
33836  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
33837  * @cfg {String} title          The title for this panel
33838  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
33839  * @cfg {String} url            Calls {@link #setUrl} with this value
33840  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
33841  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
33842  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
33843  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
33844
33845  * @constructor
33846  * Create a new ContentPanel.
33847  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
33848  * @param {String/Object} config A string to set only the title or a config object
33849  * @param {String} content (optional) Set the HTML content for this panel
33850  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
33851  */
33852 Roo.ContentPanel = function(el, config, content){
33853     
33854      
33855     /*
33856     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
33857         config = el;
33858         el = Roo.id();
33859     }
33860     if (config && config.parentLayout) { 
33861         el = config.parentLayout.el.createChild(); 
33862     }
33863     */
33864     if(el.autoCreate){ // xtype is available if this is called from factory
33865         config = el;
33866         el = Roo.id();
33867     }
33868     this.el = Roo.get(el);
33869     if(!this.el && config && config.autoCreate){
33870         if(typeof config.autoCreate == "object"){
33871             if(!config.autoCreate.id){
33872                 config.autoCreate.id = config.id||el;
33873             }
33874             this.el = Roo.DomHelper.append(document.body,
33875                         config.autoCreate, true);
33876         }else{
33877             this.el = Roo.DomHelper.append(document.body,
33878                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
33879         }
33880     }
33881     this.closable = false;
33882     this.loaded = false;
33883     this.active = false;
33884     if(typeof config == "string"){
33885         this.title = config;
33886     }else{
33887         Roo.apply(this, config);
33888     }
33889     
33890     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
33891         this.wrapEl = this.el.wrap();
33892         this.toolbar.container = this.el.insertSibling(false, 'before');
33893         this.toolbar = new Roo.Toolbar(this.toolbar);
33894     }
33895     
33896     // xtype created footer. - not sure if will work as we normally have to render first..
33897     if (this.footer && !this.footer.el && this.footer.xtype) {
33898         if (!this.wrapEl) {
33899             this.wrapEl = this.el.wrap();
33900         }
33901     
33902         this.footer.container = this.wrapEl.createChild();
33903          
33904         this.footer = Roo.factory(this.footer, Roo);
33905         
33906     }
33907     
33908     if(this.resizeEl){
33909         this.resizeEl = Roo.get(this.resizeEl, true);
33910     }else{
33911         this.resizeEl = this.el;
33912     }
33913     // handle view.xtype
33914     
33915  
33916     
33917     
33918     this.addEvents({
33919         /**
33920          * @event activate
33921          * Fires when this panel is activated. 
33922          * @param {Roo.ContentPanel} this
33923          */
33924         "activate" : true,
33925         /**
33926          * @event deactivate
33927          * Fires when this panel is activated. 
33928          * @param {Roo.ContentPanel} this
33929          */
33930         "deactivate" : true,
33931
33932         /**
33933          * @event resize
33934          * Fires when this panel is resized if fitToFrame is true.
33935          * @param {Roo.ContentPanel} this
33936          * @param {Number} width The width after any component adjustments
33937          * @param {Number} height The height after any component adjustments
33938          */
33939         "resize" : true,
33940         
33941          /**
33942          * @event render
33943          * Fires when this tab is created
33944          * @param {Roo.ContentPanel} this
33945          */
33946         "render" : true
33947         
33948         
33949         
33950     });
33951     
33952
33953     
33954     
33955     if(this.autoScroll){
33956         this.resizeEl.setStyle("overflow", "auto");
33957     } else {
33958         // fix randome scrolling
33959         this.el.on('scroll', function() {
33960             Roo.log('fix random scolling');
33961             this.scrollTo('top',0); 
33962         });
33963     }
33964     content = content || this.content;
33965     if(content){
33966         this.setContent(content);
33967     }
33968     if(config && config.url){
33969         this.setUrl(this.url, this.params, this.loadOnce);
33970     }
33971     
33972     
33973     
33974     Roo.ContentPanel.superclass.constructor.call(this);
33975     
33976     if (this.view && typeof(this.view.xtype) != 'undefined') {
33977         this.view.el = this.el.appendChild(document.createElement("div"));
33978         this.view = Roo.factory(this.view); 
33979         this.view.render  &&  this.view.render(false, '');  
33980     }
33981     
33982     
33983     this.fireEvent('render', this);
33984 };
33985
33986 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
33987     tabTip:'',
33988     setRegion : function(region){
33989         this.region = region;
33990         if(region){
33991            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
33992         }else{
33993            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
33994         } 
33995     },
33996     
33997     /**
33998      * Returns the toolbar for this Panel if one was configured. 
33999      * @return {Roo.Toolbar} 
34000      */
34001     getToolbar : function(){
34002         return this.toolbar;
34003     },
34004     
34005     setActiveState : function(active){
34006         this.active = active;
34007         if(!active){
34008             this.fireEvent("deactivate", this);
34009         }else{
34010             this.fireEvent("activate", this);
34011         }
34012     },
34013     /**
34014      * Updates this panel's element
34015      * @param {String} content The new content
34016      * @param {Boolean} loadScripts (optional) true to look for and process scripts
34017     */
34018     setContent : function(content, loadScripts){
34019         this.el.update(content, loadScripts);
34020     },
34021
34022     ignoreResize : function(w, h){
34023         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
34024             return true;
34025         }else{
34026             this.lastSize = {width: w, height: h};
34027             return false;
34028         }
34029     },
34030     /**
34031      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
34032      * @return {Roo.UpdateManager} The UpdateManager
34033      */
34034     getUpdateManager : function(){
34035         return this.el.getUpdateManager();
34036     },
34037      /**
34038      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
34039      * @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:
34040 <pre><code>
34041 panel.load({
34042     url: "your-url.php",
34043     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
34044     callback: yourFunction,
34045     scope: yourObject, //(optional scope)
34046     discardUrl: false,
34047     nocache: false,
34048     text: "Loading...",
34049     timeout: 30,
34050     scripts: false
34051 });
34052 </code></pre>
34053      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
34054      * 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.
34055      * @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}
34056      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
34057      * @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.
34058      * @return {Roo.ContentPanel} this
34059      */
34060     load : function(){
34061         var um = this.el.getUpdateManager();
34062         um.update.apply(um, arguments);
34063         return this;
34064     },
34065
34066
34067     /**
34068      * 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.
34069      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
34070      * @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)
34071      * @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)
34072      * @return {Roo.UpdateManager} The UpdateManager
34073      */
34074     setUrl : function(url, params, loadOnce){
34075         if(this.refreshDelegate){
34076             this.removeListener("activate", this.refreshDelegate);
34077         }
34078         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
34079         this.on("activate", this.refreshDelegate);
34080         return this.el.getUpdateManager();
34081     },
34082     
34083     _handleRefresh : function(url, params, loadOnce){
34084         if(!loadOnce || !this.loaded){
34085             var updater = this.el.getUpdateManager();
34086             updater.update(url, params, this._setLoaded.createDelegate(this));
34087         }
34088     },
34089     
34090     _setLoaded : function(){
34091         this.loaded = true;
34092     }, 
34093     
34094     /**
34095      * Returns this panel's id
34096      * @return {String} 
34097      */
34098     getId : function(){
34099         return this.el.id;
34100     },
34101     
34102     /** 
34103      * Returns this panel's element - used by regiosn to add.
34104      * @return {Roo.Element} 
34105      */
34106     getEl : function(){
34107         return this.wrapEl || this.el;
34108     },
34109     
34110     adjustForComponents : function(width, height)
34111     {
34112         //Roo.log('adjustForComponents ');
34113         if(this.resizeEl != this.el){
34114             width -= this.el.getFrameWidth('lr');
34115             height -= this.el.getFrameWidth('tb');
34116         }
34117         if(this.toolbar){
34118             var te = this.toolbar.getEl();
34119             height -= te.getHeight();
34120             te.setWidth(width);
34121         }
34122         if(this.footer){
34123             var te = this.footer.getEl();
34124             Roo.log("footer:" + te.getHeight());
34125             
34126             height -= te.getHeight();
34127             te.setWidth(width);
34128         }
34129         
34130         
34131         if(this.adjustments){
34132             width += this.adjustments[0];
34133             height += this.adjustments[1];
34134         }
34135         return {"width": width, "height": height};
34136     },
34137     
34138     setSize : function(width, height){
34139         if(this.fitToFrame && !this.ignoreResize(width, height)){
34140             if(this.fitContainer && this.resizeEl != this.el){
34141                 this.el.setSize(width, height);
34142             }
34143             var size = this.adjustForComponents(width, height);
34144             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
34145             this.fireEvent('resize', this, size.width, size.height);
34146         }
34147     },
34148     
34149     /**
34150      * Returns this panel's title
34151      * @return {String} 
34152      */
34153     getTitle : function(){
34154         return this.title;
34155     },
34156     
34157     /**
34158      * Set this panel's title
34159      * @param {String} title
34160      */
34161     setTitle : function(title){
34162         this.title = title;
34163         if(this.region){
34164             this.region.updatePanelTitle(this, title);
34165         }
34166     },
34167     
34168     /**
34169      * Returns true is this panel was configured to be closable
34170      * @return {Boolean} 
34171      */
34172     isClosable : function(){
34173         return this.closable;
34174     },
34175     
34176     beforeSlide : function(){
34177         this.el.clip();
34178         this.resizeEl.clip();
34179     },
34180     
34181     afterSlide : function(){
34182         this.el.unclip();
34183         this.resizeEl.unclip();
34184     },
34185     
34186     /**
34187      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
34188      *   Will fail silently if the {@link #setUrl} method has not been called.
34189      *   This does not activate the panel, just updates its content.
34190      */
34191     refresh : function(){
34192         if(this.refreshDelegate){
34193            this.loaded = false;
34194            this.refreshDelegate();
34195         }
34196     },
34197     
34198     /**
34199      * Destroys this panel
34200      */
34201     destroy : function(){
34202         this.el.removeAllListeners();
34203         var tempEl = document.createElement("span");
34204         tempEl.appendChild(this.el.dom);
34205         tempEl.innerHTML = "";
34206         this.el.remove();
34207         this.el = null;
34208     },
34209     
34210     /**
34211      * form - if the content panel contains a form - this is a reference to it.
34212      * @type {Roo.form.Form}
34213      */
34214     form : false,
34215     /**
34216      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
34217      *    This contains a reference to it.
34218      * @type {Roo.View}
34219      */
34220     view : false,
34221     
34222       /**
34223      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
34224      * <pre><code>
34225
34226 layout.addxtype({
34227        xtype : 'Form',
34228        items: [ .... ]
34229    }
34230 );
34231
34232 </code></pre>
34233      * @param {Object} cfg Xtype definition of item to add.
34234      */
34235     
34236     addxtype : function(cfg) {
34237         // add form..
34238         if (cfg.xtype.match(/^Form$/)) {
34239             
34240             var el;
34241             //if (this.footer) {
34242             //    el = this.footer.container.insertSibling(false, 'before');
34243             //} else {
34244                 el = this.el.createChild();
34245             //}
34246
34247             this.form = new  Roo.form.Form(cfg);
34248             
34249             
34250             if ( this.form.allItems.length) this.form.render(el.dom);
34251             return this.form;
34252         }
34253         // should only have one of theses..
34254         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
34255             // views.. should not be just added - used named prop 'view''
34256             
34257             cfg.el = this.el.appendChild(document.createElement("div"));
34258             // factory?
34259             
34260             var ret = new Roo.factory(cfg);
34261              
34262              ret.render && ret.render(false, ''); // render blank..
34263             this.view = ret;
34264             return ret;
34265         }
34266         return false;
34267     }
34268 });
34269
34270 /**
34271  * @class Roo.GridPanel
34272  * @extends Roo.ContentPanel
34273  * @constructor
34274  * Create a new GridPanel.
34275  * @param {Roo.grid.Grid} grid The grid for this panel
34276  * @param {String/Object} config A string to set only the panel's title, or a config object
34277  */
34278 Roo.GridPanel = function(grid, config){
34279     
34280   
34281     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
34282         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
34283         
34284     this.wrapper.dom.appendChild(grid.getGridEl().dom);
34285     
34286     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
34287     
34288     if(this.toolbar){
34289         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
34290     }
34291     // xtype created footer. - not sure if will work as we normally have to render first..
34292     if (this.footer && !this.footer.el && this.footer.xtype) {
34293         
34294         this.footer.container = this.grid.getView().getFooterPanel(true);
34295         this.footer.dataSource = this.grid.dataSource;
34296         this.footer = Roo.factory(this.footer, Roo);
34297         
34298     }
34299     
34300     grid.monitorWindowResize = false; // turn off autosizing
34301     grid.autoHeight = false;
34302     grid.autoWidth = false;
34303     this.grid = grid;
34304     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
34305 };
34306
34307 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
34308     getId : function(){
34309         return this.grid.id;
34310     },
34311     
34312     /**
34313      * Returns the grid for this panel
34314      * @return {Roo.grid.Grid} 
34315      */
34316     getGrid : function(){
34317         return this.grid;    
34318     },
34319     
34320     setSize : function(width, height){
34321         if(!this.ignoreResize(width, height)){
34322             var grid = this.grid;
34323             var size = this.adjustForComponents(width, height);
34324             grid.getGridEl().setSize(size.width, size.height);
34325             grid.autoSize();
34326         }
34327     },
34328     
34329     beforeSlide : function(){
34330         this.grid.getView().scroller.clip();
34331     },
34332     
34333     afterSlide : function(){
34334         this.grid.getView().scroller.unclip();
34335     },
34336     
34337     destroy : function(){
34338         this.grid.destroy();
34339         delete this.grid;
34340         Roo.GridPanel.superclass.destroy.call(this); 
34341     }
34342 });
34343
34344
34345 /**
34346  * @class Roo.NestedLayoutPanel
34347  * @extends Roo.ContentPanel
34348  * @constructor
34349  * Create a new NestedLayoutPanel.
34350  * 
34351  * 
34352  * @param {Roo.BorderLayout} layout The layout for this panel
34353  * @param {String/Object} config A string to set only the title or a config object
34354  */
34355 Roo.NestedLayoutPanel = function(layout, config)
34356 {
34357     // construct with only one argument..
34358     /* FIXME - implement nicer consturctors
34359     if (layout.layout) {
34360         config = layout;
34361         layout = config.layout;
34362         delete config.layout;
34363     }
34364     if (layout.xtype && !layout.getEl) {
34365         // then layout needs constructing..
34366         layout = Roo.factory(layout, Roo);
34367     }
34368     */
34369     
34370     
34371     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
34372     
34373     layout.monitorWindowResize = false; // turn off autosizing
34374     this.layout = layout;
34375     this.layout.getEl().addClass("x-layout-nested-layout");
34376     
34377     
34378     
34379     
34380 };
34381
34382 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
34383
34384     setSize : function(width, height){
34385         if(!this.ignoreResize(width, height)){
34386             var size = this.adjustForComponents(width, height);
34387             var el = this.layout.getEl();
34388             el.setSize(size.width, size.height);
34389             var touch = el.dom.offsetWidth;
34390             this.layout.layout();
34391             // ie requires a double layout on the first pass
34392             if(Roo.isIE && !this.initialized){
34393                 this.initialized = true;
34394                 this.layout.layout();
34395             }
34396         }
34397     },
34398     
34399     // activate all subpanels if not currently active..
34400     
34401     setActiveState : function(active){
34402         this.active = active;
34403         if(!active){
34404             this.fireEvent("deactivate", this);
34405             return;
34406         }
34407         
34408         this.fireEvent("activate", this);
34409         // not sure if this should happen before or after..
34410         if (!this.layout) {
34411             return; // should not happen..
34412         }
34413         var reg = false;
34414         for (var r in this.layout.regions) {
34415             reg = this.layout.getRegion(r);
34416             if (reg.getActivePanel()) {
34417                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
34418                 reg.setActivePanel(reg.getActivePanel());
34419                 continue;
34420             }
34421             if (!reg.panels.length) {
34422                 continue;
34423             }
34424             reg.showPanel(reg.getPanel(0));
34425         }
34426         
34427         
34428         
34429         
34430     },
34431     
34432     /**
34433      * Returns the nested BorderLayout for this panel
34434      * @return {Roo.BorderLayout} 
34435      */
34436     getLayout : function(){
34437         return this.layout;
34438     },
34439     
34440      /**
34441      * Adds a xtype elements to the layout of the nested panel
34442      * <pre><code>
34443
34444 panel.addxtype({
34445        xtype : 'ContentPanel',
34446        region: 'west',
34447        items: [ .... ]
34448    }
34449 );
34450
34451 panel.addxtype({
34452         xtype : 'NestedLayoutPanel',
34453         region: 'west',
34454         layout: {
34455            center: { },
34456            west: { }   
34457         },
34458         items : [ ... list of content panels or nested layout panels.. ]
34459    }
34460 );
34461 </code></pre>
34462      * @param {Object} cfg Xtype definition of item to add.
34463      */
34464     addxtype : function(cfg) {
34465         return this.layout.addxtype(cfg);
34466     
34467     }
34468 });
34469
34470 Roo.ScrollPanel = function(el, config, content){
34471     config = config || {};
34472     config.fitToFrame = true;
34473     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
34474     
34475     this.el.dom.style.overflow = "hidden";
34476     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
34477     this.el.removeClass("x-layout-inactive-content");
34478     this.el.on("mousewheel", this.onWheel, this);
34479
34480     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
34481     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
34482     up.unselectable(); down.unselectable();
34483     up.on("click", this.scrollUp, this);
34484     down.on("click", this.scrollDown, this);
34485     up.addClassOnOver("x-scroller-btn-over");
34486     down.addClassOnOver("x-scroller-btn-over");
34487     up.addClassOnClick("x-scroller-btn-click");
34488     down.addClassOnClick("x-scroller-btn-click");
34489     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
34490
34491     this.resizeEl = this.el;
34492     this.el = wrap; this.up = up; this.down = down;
34493 };
34494
34495 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
34496     increment : 100,
34497     wheelIncrement : 5,
34498     scrollUp : function(){
34499         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
34500     },
34501
34502     scrollDown : function(){
34503         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
34504     },
34505
34506     afterScroll : function(){
34507         var el = this.resizeEl;
34508         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
34509         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
34510         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
34511     },
34512
34513     setSize : function(){
34514         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
34515         this.afterScroll();
34516     },
34517
34518     onWheel : function(e){
34519         var d = e.getWheelDelta();
34520         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
34521         this.afterScroll();
34522         e.stopEvent();
34523     },
34524
34525     setContent : function(content, loadScripts){
34526         this.resizeEl.update(content, loadScripts);
34527     }
34528
34529 });
34530
34531
34532
34533
34534
34535
34536
34537
34538
34539 /**
34540  * @class Roo.TreePanel
34541  * @extends Roo.ContentPanel
34542  * @constructor
34543  * Create a new TreePanel. - defaults to fit/scoll contents.
34544  * @param {String/Object} config A string to set only the panel's title, or a config object
34545  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
34546  */
34547 Roo.TreePanel = function(config){
34548     var el = config.el;
34549     var tree = config.tree;
34550     delete config.tree; 
34551     delete config.el; // hopefull!
34552     
34553     // wrapper for IE7 strict & safari scroll issue
34554     
34555     var treeEl = el.createChild();
34556     config.resizeEl = treeEl;
34557     
34558     
34559     
34560     Roo.TreePanel.superclass.constructor.call(this, el, config);
34561  
34562  
34563     this.tree = new Roo.tree.TreePanel(treeEl , tree);
34564     //console.log(tree);
34565     this.on('activate', function()
34566     {
34567         if (this.tree.rendered) {
34568             return;
34569         }
34570         //console.log('render tree');
34571         this.tree.render();
34572     });
34573     // this should not be needed.. - it's actually the 'el' that resizes?
34574     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
34575     
34576     //this.on('resize',  function (cp, w, h) {
34577     //        this.tree.innerCt.setWidth(w);
34578     //        this.tree.innerCt.setHeight(h);
34579     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
34580     //});
34581
34582         
34583     
34584 };
34585
34586 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
34587     fitToFrame : true,
34588     autoScroll : true
34589 });
34590
34591
34592
34593
34594
34595
34596
34597
34598
34599
34600
34601 /*
34602  * Based on:
34603  * Ext JS Library 1.1.1
34604  * Copyright(c) 2006-2007, Ext JS, LLC.
34605  *
34606  * Originally Released Under LGPL - original licence link has changed is not relivant.
34607  *
34608  * Fork - LGPL
34609  * <script type="text/javascript">
34610  */
34611  
34612
34613 /**
34614  * @class Roo.ReaderLayout
34615  * @extends Roo.BorderLayout
34616  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
34617  * center region containing two nested regions (a top one for a list view and one for item preview below),
34618  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
34619  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
34620  * expedites the setup of the overall layout and regions for this common application style.
34621  * Example:
34622  <pre><code>
34623 var reader = new Roo.ReaderLayout();
34624 var CP = Roo.ContentPanel;  // shortcut for adding
34625
34626 reader.beginUpdate();
34627 reader.add("north", new CP("north", "North"));
34628 reader.add("west", new CP("west", {title: "West"}));
34629 reader.add("east", new CP("east", {title: "East"}));
34630
34631 reader.regions.listView.add(new CP("listView", "List"));
34632 reader.regions.preview.add(new CP("preview", "Preview"));
34633 reader.endUpdate();
34634 </code></pre>
34635 * @constructor
34636 * Create a new ReaderLayout
34637 * @param {Object} config Configuration options
34638 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
34639 * document.body if omitted)
34640 */
34641 Roo.ReaderLayout = function(config, renderTo){
34642     var c = config || {size:{}};
34643     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
34644         north: c.north !== false ? Roo.apply({
34645             split:false,
34646             initialSize: 32,
34647             titlebar: false
34648         }, c.north) : false,
34649         west: c.west !== false ? Roo.apply({
34650             split:true,
34651             initialSize: 200,
34652             minSize: 175,
34653             maxSize: 400,
34654             titlebar: true,
34655             collapsible: true,
34656             animate: true,
34657             margins:{left:5,right:0,bottom:5,top:5},
34658             cmargins:{left:5,right:5,bottom:5,top:5}
34659         }, c.west) : false,
34660         east: c.east !== false ? Roo.apply({
34661             split:true,
34662             initialSize: 200,
34663             minSize: 175,
34664             maxSize: 400,
34665             titlebar: true,
34666             collapsible: true,
34667             animate: true,
34668             margins:{left:0,right:5,bottom:5,top:5},
34669             cmargins:{left:5,right:5,bottom:5,top:5}
34670         }, c.east) : false,
34671         center: Roo.apply({
34672             tabPosition: 'top',
34673             autoScroll:false,
34674             closeOnTab: true,
34675             titlebar:false,
34676             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
34677         }, c.center)
34678     });
34679
34680     this.el.addClass('x-reader');
34681
34682     this.beginUpdate();
34683
34684     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
34685         south: c.preview !== false ? Roo.apply({
34686             split:true,
34687             initialSize: 200,
34688             minSize: 100,
34689             autoScroll:true,
34690             collapsible:true,
34691             titlebar: true,
34692             cmargins:{top:5,left:0, right:0, bottom:0}
34693         }, c.preview) : false,
34694         center: Roo.apply({
34695             autoScroll:false,
34696             titlebar:false,
34697             minHeight:200
34698         }, c.listView)
34699     });
34700     this.add('center', new Roo.NestedLayoutPanel(inner,
34701             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
34702
34703     this.endUpdate();
34704
34705     this.regions.preview = inner.getRegion('south');
34706     this.regions.listView = inner.getRegion('center');
34707 };
34708
34709 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
34710  * Based on:
34711  * Ext JS Library 1.1.1
34712  * Copyright(c) 2006-2007, Ext JS, LLC.
34713  *
34714  * Originally Released Under LGPL - original licence link has changed is not relivant.
34715  *
34716  * Fork - LGPL
34717  * <script type="text/javascript">
34718  */
34719  
34720 /**
34721  * @class Roo.grid.Grid
34722  * @extends Roo.util.Observable
34723  * This class represents the primary interface of a component based grid control.
34724  * <br><br>Usage:<pre><code>
34725  var grid = new Roo.grid.Grid("my-container-id", {
34726      ds: myDataStore,
34727      cm: myColModel,
34728      selModel: mySelectionModel,
34729      autoSizeColumns: true,
34730      monitorWindowResize: false,
34731      trackMouseOver: true
34732  });
34733  // set any options
34734  grid.render();
34735  * </code></pre>
34736  * <b>Common Problems:</b><br/>
34737  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
34738  * element will correct this<br/>
34739  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
34740  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
34741  * are unpredictable.<br/>
34742  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
34743  * grid to calculate dimensions/offsets.<br/>
34744   * @constructor
34745  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
34746  * The container MUST have some type of size defined for the grid to fill. The container will be
34747  * automatically set to position relative if it isn't already.
34748  * @param {Object} config A config object that sets properties on this grid.
34749  */
34750 Roo.grid.Grid = function(container, config){
34751         // initialize the container
34752         this.container = Roo.get(container);
34753         this.container.update("");
34754         this.container.setStyle("overflow", "hidden");
34755     this.container.addClass('x-grid-container');
34756
34757     this.id = this.container.id;
34758
34759     Roo.apply(this, config);
34760     // check and correct shorthanded configs
34761     if(this.ds){
34762         this.dataSource = this.ds;
34763         delete this.ds;
34764     }
34765     if(this.cm){
34766         this.colModel = this.cm;
34767         delete this.cm;
34768     }
34769     if(this.sm){
34770         this.selModel = this.sm;
34771         delete this.sm;
34772     }
34773
34774     if (this.selModel) {
34775         this.selModel = Roo.factory(this.selModel, Roo.grid);
34776         this.sm = this.selModel;
34777         this.sm.xmodule = this.xmodule || false;
34778     }
34779     if (typeof(this.colModel.config) == 'undefined') {
34780         this.colModel = new Roo.grid.ColumnModel(this.colModel);
34781         this.cm = this.colModel;
34782         this.cm.xmodule = this.xmodule || false;
34783     }
34784     if (this.dataSource) {
34785         this.dataSource= Roo.factory(this.dataSource, Roo.data);
34786         this.ds = this.dataSource;
34787         this.ds.xmodule = this.xmodule || false;
34788          
34789     }
34790     
34791     
34792     
34793     if(this.width){
34794         this.container.setWidth(this.width);
34795     }
34796
34797     if(this.height){
34798         this.container.setHeight(this.height);
34799     }
34800     /** @private */
34801         this.addEvents({
34802         // raw events
34803         /**
34804          * @event click
34805          * The raw click event for the entire grid.
34806          * @param {Roo.EventObject} e
34807          */
34808         "click" : true,
34809         /**
34810          * @event dblclick
34811          * The raw dblclick event for the entire grid.
34812          * @param {Roo.EventObject} e
34813          */
34814         "dblclick" : true,
34815         /**
34816          * @event contextmenu
34817          * The raw contextmenu event for the entire grid.
34818          * @param {Roo.EventObject} e
34819          */
34820         "contextmenu" : true,
34821         /**
34822          * @event mousedown
34823          * The raw mousedown event for the entire grid.
34824          * @param {Roo.EventObject} e
34825          */
34826         "mousedown" : true,
34827         /**
34828          * @event mouseup
34829          * The raw mouseup event for the entire grid.
34830          * @param {Roo.EventObject} e
34831          */
34832         "mouseup" : true,
34833         /**
34834          * @event mouseover
34835          * The raw mouseover event for the entire grid.
34836          * @param {Roo.EventObject} e
34837          */
34838         "mouseover" : true,
34839         /**
34840          * @event mouseout
34841          * The raw mouseout event for the entire grid.
34842          * @param {Roo.EventObject} e
34843          */
34844         "mouseout" : true,
34845         /**
34846          * @event keypress
34847          * The raw keypress event for the entire grid.
34848          * @param {Roo.EventObject} e
34849          */
34850         "keypress" : true,
34851         /**
34852          * @event keydown
34853          * The raw keydown event for the entire grid.
34854          * @param {Roo.EventObject} e
34855          */
34856         "keydown" : true,
34857
34858         // custom events
34859
34860         /**
34861          * @event cellclick
34862          * Fires when a cell is clicked
34863          * @param {Grid} this
34864          * @param {Number} rowIndex
34865          * @param {Number} columnIndex
34866          * @param {Roo.EventObject} e
34867          */
34868         "cellclick" : true,
34869         /**
34870          * @event celldblclick
34871          * Fires when a cell is double clicked
34872          * @param {Grid} this
34873          * @param {Number} rowIndex
34874          * @param {Number} columnIndex
34875          * @param {Roo.EventObject} e
34876          */
34877         "celldblclick" : true,
34878         /**
34879          * @event rowclick
34880          * Fires when a row is clicked
34881          * @param {Grid} this
34882          * @param {Number} rowIndex
34883          * @param {Roo.EventObject} e
34884          */
34885         "rowclick" : true,
34886         /**
34887          * @event rowdblclick
34888          * Fires when a row is double clicked
34889          * @param {Grid} this
34890          * @param {Number} rowIndex
34891          * @param {Roo.EventObject} e
34892          */
34893         "rowdblclick" : true,
34894         /**
34895          * @event headerclick
34896          * Fires when a header is clicked
34897          * @param {Grid} this
34898          * @param {Number} columnIndex
34899          * @param {Roo.EventObject} e
34900          */
34901         "headerclick" : true,
34902         /**
34903          * @event headerdblclick
34904          * Fires when a header cell is double clicked
34905          * @param {Grid} this
34906          * @param {Number} columnIndex
34907          * @param {Roo.EventObject} e
34908          */
34909         "headerdblclick" : true,
34910         /**
34911          * @event rowcontextmenu
34912          * Fires when a row is right clicked
34913          * @param {Grid} this
34914          * @param {Number} rowIndex
34915          * @param {Roo.EventObject} e
34916          */
34917         "rowcontextmenu" : true,
34918         /**
34919          * @event cellcontextmenu
34920          * Fires when a cell is right clicked
34921          * @param {Grid} this
34922          * @param {Number} rowIndex
34923          * @param {Number} cellIndex
34924          * @param {Roo.EventObject} e
34925          */
34926          "cellcontextmenu" : true,
34927         /**
34928          * @event headercontextmenu
34929          * Fires when a header is right clicked
34930          * @param {Grid} this
34931          * @param {Number} columnIndex
34932          * @param {Roo.EventObject} e
34933          */
34934         "headercontextmenu" : true,
34935         /**
34936          * @event bodyscroll
34937          * Fires when the body element is scrolled
34938          * @param {Number} scrollLeft
34939          * @param {Number} scrollTop
34940          */
34941         "bodyscroll" : true,
34942         /**
34943          * @event columnresize
34944          * Fires when the user resizes a column
34945          * @param {Number} columnIndex
34946          * @param {Number} newSize
34947          */
34948         "columnresize" : true,
34949         /**
34950          * @event columnmove
34951          * Fires when the user moves a column
34952          * @param {Number} oldIndex
34953          * @param {Number} newIndex
34954          */
34955         "columnmove" : true,
34956         /**
34957          * @event startdrag
34958          * Fires when row(s) start being dragged
34959          * @param {Grid} this
34960          * @param {Roo.GridDD} dd The drag drop object
34961          * @param {event} e The raw browser event
34962          */
34963         "startdrag" : true,
34964         /**
34965          * @event enddrag
34966          * Fires when a drag operation is complete
34967          * @param {Grid} this
34968          * @param {Roo.GridDD} dd The drag drop object
34969          * @param {event} e The raw browser event
34970          */
34971         "enddrag" : true,
34972         /**
34973          * @event dragdrop
34974          * Fires when dragged row(s) are dropped on a valid DD target
34975          * @param {Grid} this
34976          * @param {Roo.GridDD} dd The drag drop object
34977          * @param {String} targetId The target drag drop object
34978          * @param {event} e The raw browser event
34979          */
34980         "dragdrop" : true,
34981         /**
34982          * @event dragover
34983          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
34984          * @param {Grid} this
34985          * @param {Roo.GridDD} dd The drag drop object
34986          * @param {String} targetId The target drag drop object
34987          * @param {event} e The raw browser event
34988          */
34989         "dragover" : true,
34990         /**
34991          * @event dragenter
34992          *  Fires when the dragged row(s) first cross another DD target while being dragged
34993          * @param {Grid} this
34994          * @param {Roo.GridDD} dd The drag drop object
34995          * @param {String} targetId The target drag drop object
34996          * @param {event} e The raw browser event
34997          */
34998         "dragenter" : true,
34999         /**
35000          * @event dragout
35001          * Fires when the dragged row(s) leave another DD target while being dragged
35002          * @param {Grid} this
35003          * @param {Roo.GridDD} dd The drag drop object
35004          * @param {String} targetId The target drag drop object
35005          * @param {event} e The raw browser event
35006          */
35007         "dragout" : true,
35008         /**
35009          * @event rowclass
35010          * Fires when a row is rendered, so you can change add a style to it.
35011          * @param {GridView} gridview   The grid view
35012          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
35013          */
35014         'rowclass' : true,
35015
35016         /**
35017          * @event render
35018          * Fires when the grid is rendered
35019          * @param {Grid} grid
35020          */
35021         'render' : true
35022     });
35023
35024     Roo.grid.Grid.superclass.constructor.call(this);
35025 };
35026 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
35027     
35028     /**
35029      * @cfg {String} ddGroup - drag drop group.
35030      */
35031
35032     /**
35033      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
35034      */
35035     minColumnWidth : 25,
35036
35037     /**
35038      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
35039      * <b>on initial render.</b> It is more efficient to explicitly size the columns
35040      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
35041      */
35042     autoSizeColumns : false,
35043
35044     /**
35045      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
35046      */
35047     autoSizeHeaders : true,
35048
35049     /**
35050      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
35051      */
35052     monitorWindowResize : true,
35053
35054     /**
35055      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
35056      * rows measured to get a columns size. Default is 0 (all rows).
35057      */
35058     maxRowsToMeasure : 0,
35059
35060     /**
35061      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
35062      */
35063     trackMouseOver : true,
35064
35065     /**
35066     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
35067     */
35068     
35069     /**
35070     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
35071     */
35072     enableDragDrop : false,
35073     
35074     /**
35075     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
35076     */
35077     enableColumnMove : true,
35078     
35079     /**
35080     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
35081     */
35082     enableColumnHide : true,
35083     
35084     /**
35085     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
35086     */
35087     enableRowHeightSync : false,
35088     
35089     /**
35090     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
35091     */
35092     stripeRows : true,
35093     
35094     /**
35095     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
35096     */
35097     autoHeight : false,
35098
35099     /**
35100      * @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.
35101      */
35102     autoExpandColumn : false,
35103
35104     /**
35105     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
35106     * Default is 50.
35107     */
35108     autoExpandMin : 50,
35109
35110     /**
35111     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
35112     */
35113     autoExpandMax : 1000,
35114
35115     /**
35116     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
35117     */
35118     view : null,
35119
35120     /**
35121     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
35122     */
35123     loadMask : false,
35124     /**
35125     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
35126     */
35127     dropTarget: false,
35128     
35129    
35130     
35131     // private
35132     rendered : false,
35133
35134     /**
35135     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
35136     * of a fixed width. Default is false.
35137     */
35138     /**
35139     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
35140     */
35141     /**
35142      * Called once after all setup has been completed and the grid is ready to be rendered.
35143      * @return {Roo.grid.Grid} this
35144      */
35145     render : function()
35146     {
35147         var c = this.container;
35148         // try to detect autoHeight/width mode
35149         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
35150             this.autoHeight = true;
35151         }
35152         var view = this.getView();
35153         view.init(this);
35154
35155         c.on("click", this.onClick, this);
35156         c.on("dblclick", this.onDblClick, this);
35157         c.on("contextmenu", this.onContextMenu, this);
35158         c.on("keydown", this.onKeyDown, this);
35159
35160         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
35161
35162         this.getSelectionModel().init(this);
35163
35164         view.render();
35165
35166         if(this.loadMask){
35167             this.loadMask = new Roo.LoadMask(this.container,
35168                     Roo.apply({store:this.dataSource}, this.loadMask));
35169         }
35170         
35171         
35172         if (this.toolbar && this.toolbar.xtype) {
35173             this.toolbar.container = this.getView().getHeaderPanel(true);
35174             this.toolbar = new Roo.Toolbar(this.toolbar);
35175         }
35176         if (this.footer && this.footer.xtype) {
35177             this.footer.dataSource = this.getDataSource();
35178             this.footer.container = this.getView().getFooterPanel(true);
35179             this.footer = Roo.factory(this.footer, Roo);
35180         }
35181         if (this.dropTarget && this.dropTarget.xtype) {
35182             delete this.dropTarget.xtype;
35183             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
35184         }
35185         
35186         
35187         this.rendered = true;
35188         this.fireEvent('render', this);
35189         return this;
35190     },
35191
35192         /**
35193          * Reconfigures the grid to use a different Store and Column Model.
35194          * The View will be bound to the new objects and refreshed.
35195          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
35196          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
35197          */
35198     reconfigure : function(dataSource, colModel){
35199         if(this.loadMask){
35200             this.loadMask.destroy();
35201             this.loadMask = new Roo.LoadMask(this.container,
35202                     Roo.apply({store:dataSource}, this.loadMask));
35203         }
35204         this.view.bind(dataSource, colModel);
35205         this.dataSource = dataSource;
35206         this.colModel = colModel;
35207         this.view.refresh(true);
35208     },
35209
35210     // private
35211     onKeyDown : function(e){
35212         this.fireEvent("keydown", e);
35213     },
35214
35215     /**
35216      * Destroy this grid.
35217      * @param {Boolean} removeEl True to remove the element
35218      */
35219     destroy : function(removeEl, keepListeners){
35220         if(this.loadMask){
35221             this.loadMask.destroy();
35222         }
35223         var c = this.container;
35224         c.removeAllListeners();
35225         this.view.destroy();
35226         this.colModel.purgeListeners();
35227         if(!keepListeners){
35228             this.purgeListeners();
35229         }
35230         c.update("");
35231         if(removeEl === true){
35232             c.remove();
35233         }
35234     },
35235
35236     // private
35237     processEvent : function(name, e){
35238         this.fireEvent(name, e);
35239         var t = e.getTarget();
35240         var v = this.view;
35241         var header = v.findHeaderIndex(t);
35242         if(header !== false){
35243             this.fireEvent("header" + name, this, header, e);
35244         }else{
35245             var row = v.findRowIndex(t);
35246             var cell = v.findCellIndex(t);
35247             if(row !== false){
35248                 this.fireEvent("row" + name, this, row, e);
35249                 if(cell !== false){
35250                     this.fireEvent("cell" + name, this, row, cell, e);
35251                 }
35252             }
35253         }
35254     },
35255
35256     // private
35257     onClick : function(e){
35258         this.processEvent("click", e);
35259     },
35260
35261     // private
35262     onContextMenu : function(e, t){
35263         this.processEvent("contextmenu", e);
35264     },
35265
35266     // private
35267     onDblClick : function(e){
35268         this.processEvent("dblclick", e);
35269     },
35270
35271     // private
35272     walkCells : function(row, col, step, fn, scope){
35273         var cm = this.colModel, clen = cm.getColumnCount();
35274         var ds = this.dataSource, rlen = ds.getCount(), first = true;
35275         if(step < 0){
35276             if(col < 0){
35277                 row--;
35278                 first = false;
35279             }
35280             while(row >= 0){
35281                 if(!first){
35282                     col = clen-1;
35283                 }
35284                 first = false;
35285                 while(col >= 0){
35286                     if(fn.call(scope || this, row, col, cm) === true){
35287                         return [row, col];
35288                     }
35289                     col--;
35290                 }
35291                 row--;
35292             }
35293         } else {
35294             if(col >= clen){
35295                 row++;
35296                 first = false;
35297             }
35298             while(row < rlen){
35299                 if(!first){
35300                     col = 0;
35301                 }
35302                 first = false;
35303                 while(col < clen){
35304                     if(fn.call(scope || this, row, col, cm) === true){
35305                         return [row, col];
35306                     }
35307                     col++;
35308                 }
35309                 row++;
35310             }
35311         }
35312         return null;
35313     },
35314
35315     // private
35316     getSelections : function(){
35317         return this.selModel.getSelections();
35318     },
35319
35320     /**
35321      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
35322      * but if manual update is required this method will initiate it.
35323      */
35324     autoSize : function(){
35325         if(this.rendered){
35326             this.view.layout();
35327             if(this.view.adjustForScroll){
35328                 this.view.adjustForScroll();
35329             }
35330         }
35331     },
35332
35333     /**
35334      * Returns the grid's underlying element.
35335      * @return {Element} The element
35336      */
35337     getGridEl : function(){
35338         return this.container;
35339     },
35340
35341     // private for compatibility, overridden by editor grid
35342     stopEditing : function(){},
35343
35344     /**
35345      * Returns the grid's SelectionModel.
35346      * @return {SelectionModel}
35347      */
35348     getSelectionModel : function(){
35349         if(!this.selModel){
35350             this.selModel = new Roo.grid.RowSelectionModel();
35351         }
35352         return this.selModel;
35353     },
35354
35355     /**
35356      * Returns the grid's DataSource.
35357      * @return {DataSource}
35358      */
35359     getDataSource : function(){
35360         return this.dataSource;
35361     },
35362
35363     /**
35364      * Returns the grid's ColumnModel.
35365      * @return {ColumnModel}
35366      */
35367     getColumnModel : function(){
35368         return this.colModel;
35369     },
35370
35371     /**
35372      * Returns the grid's GridView object.
35373      * @return {GridView}
35374      */
35375     getView : function(){
35376         if(!this.view){
35377             this.view = new Roo.grid.GridView(this.viewConfig);
35378         }
35379         return this.view;
35380     },
35381     /**
35382      * Called to get grid's drag proxy text, by default returns this.ddText.
35383      * @return {String}
35384      */
35385     getDragDropText : function(){
35386         var count = this.selModel.getCount();
35387         return String.format(this.ddText, count, count == 1 ? '' : 's');
35388     }
35389 });
35390 /**
35391  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
35392  * %0 is replaced with the number of selected rows.
35393  * @type String
35394  */
35395 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
35396  * Based on:
35397  * Ext JS Library 1.1.1
35398  * Copyright(c) 2006-2007, Ext JS, LLC.
35399  *
35400  * Originally Released Under LGPL - original licence link has changed is not relivant.
35401  *
35402  * Fork - LGPL
35403  * <script type="text/javascript">
35404  */
35405  
35406 Roo.grid.AbstractGridView = function(){
35407         this.grid = null;
35408         
35409         this.events = {
35410             "beforerowremoved" : true,
35411             "beforerowsinserted" : true,
35412             "beforerefresh" : true,
35413             "rowremoved" : true,
35414             "rowsinserted" : true,
35415             "rowupdated" : true,
35416             "refresh" : true
35417         };
35418     Roo.grid.AbstractGridView.superclass.constructor.call(this);
35419 };
35420
35421 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
35422     rowClass : "x-grid-row",
35423     cellClass : "x-grid-cell",
35424     tdClass : "x-grid-td",
35425     hdClass : "x-grid-hd",
35426     splitClass : "x-grid-hd-split",
35427     
35428         init: function(grid){
35429         this.grid = grid;
35430                 var cid = this.grid.getGridEl().id;
35431         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
35432         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
35433         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
35434         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
35435         },
35436         
35437         getColumnRenderers : function(){
35438         var renderers = [];
35439         var cm = this.grid.colModel;
35440         var colCount = cm.getColumnCount();
35441         for(var i = 0; i < colCount; i++){
35442             renderers[i] = cm.getRenderer(i);
35443         }
35444         return renderers;
35445     },
35446     
35447     getColumnIds : function(){
35448         var ids = [];
35449         var cm = this.grid.colModel;
35450         var colCount = cm.getColumnCount();
35451         for(var i = 0; i < colCount; i++){
35452             ids[i] = cm.getColumnId(i);
35453         }
35454         return ids;
35455     },
35456     
35457     getDataIndexes : function(){
35458         if(!this.indexMap){
35459             this.indexMap = this.buildIndexMap();
35460         }
35461         return this.indexMap.colToData;
35462     },
35463     
35464     getColumnIndexByDataIndex : function(dataIndex){
35465         if(!this.indexMap){
35466             this.indexMap = this.buildIndexMap();
35467         }
35468         return this.indexMap.dataToCol[dataIndex];
35469     },
35470     
35471     /**
35472      * Set a css style for a column dynamically. 
35473      * @param {Number} colIndex The index of the column
35474      * @param {String} name The css property name
35475      * @param {String} value The css value
35476      */
35477     setCSSStyle : function(colIndex, name, value){
35478         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
35479         Roo.util.CSS.updateRule(selector, name, value);
35480     },
35481     
35482     generateRules : function(cm){
35483         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
35484         Roo.util.CSS.removeStyleSheet(rulesId);
35485         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
35486             var cid = cm.getColumnId(i);
35487             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
35488                          this.tdSelector, cid, " {\n}\n",
35489                          this.hdSelector, cid, " {\n}\n",
35490                          this.splitSelector, cid, " {\n}\n");
35491         }
35492         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
35493     }
35494 });/*
35495  * Based on:
35496  * Ext JS Library 1.1.1
35497  * Copyright(c) 2006-2007, Ext JS, LLC.
35498  *
35499  * Originally Released Under LGPL - original licence link has changed is not relivant.
35500  *
35501  * Fork - LGPL
35502  * <script type="text/javascript">
35503  */
35504
35505 // private
35506 // This is a support class used internally by the Grid components
35507 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
35508     this.grid = grid;
35509     this.view = grid.getView();
35510     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
35511     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
35512     if(hd2){
35513         this.setHandleElId(Roo.id(hd));
35514         this.setOuterHandleElId(Roo.id(hd2));
35515     }
35516     this.scroll = false;
35517 };
35518 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
35519     maxDragWidth: 120,
35520     getDragData : function(e){
35521         var t = Roo.lib.Event.getTarget(e);
35522         var h = this.view.findHeaderCell(t);
35523         if(h){
35524             return {ddel: h.firstChild, header:h};
35525         }
35526         return false;
35527     },
35528
35529     onInitDrag : function(e){
35530         this.view.headersDisabled = true;
35531         var clone = this.dragData.ddel.cloneNode(true);
35532         clone.id = Roo.id();
35533         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
35534         this.proxy.update(clone);
35535         return true;
35536     },
35537
35538     afterValidDrop : function(){
35539         var v = this.view;
35540         setTimeout(function(){
35541             v.headersDisabled = false;
35542         }, 50);
35543     },
35544
35545     afterInvalidDrop : function(){
35546         var v = this.view;
35547         setTimeout(function(){
35548             v.headersDisabled = false;
35549         }, 50);
35550     }
35551 });
35552 /*
35553  * Based on:
35554  * Ext JS Library 1.1.1
35555  * Copyright(c) 2006-2007, Ext JS, LLC.
35556  *
35557  * Originally Released Under LGPL - original licence link has changed is not relivant.
35558  *
35559  * Fork - LGPL
35560  * <script type="text/javascript">
35561  */
35562 // private
35563 // This is a support class used internally by the Grid components
35564 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
35565     this.grid = grid;
35566     this.view = grid.getView();
35567     // split the proxies so they don't interfere with mouse events
35568     this.proxyTop = Roo.DomHelper.append(document.body, {
35569         cls:"col-move-top", html:"&#160;"
35570     }, true);
35571     this.proxyBottom = Roo.DomHelper.append(document.body, {
35572         cls:"col-move-bottom", html:"&#160;"
35573     }, true);
35574     this.proxyTop.hide = this.proxyBottom.hide = function(){
35575         this.setLeftTop(-100,-100);
35576         this.setStyle("visibility", "hidden");
35577     };
35578     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
35579     // temporarily disabled
35580     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
35581     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
35582 };
35583 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
35584     proxyOffsets : [-4, -9],
35585     fly: Roo.Element.fly,
35586
35587     getTargetFromEvent : function(e){
35588         var t = Roo.lib.Event.getTarget(e);
35589         var cindex = this.view.findCellIndex(t);
35590         if(cindex !== false){
35591             return this.view.getHeaderCell(cindex);
35592         }
35593         return null;
35594     },
35595
35596     nextVisible : function(h){
35597         var v = this.view, cm = this.grid.colModel;
35598         h = h.nextSibling;
35599         while(h){
35600             if(!cm.isHidden(v.getCellIndex(h))){
35601                 return h;
35602             }
35603             h = h.nextSibling;
35604         }
35605         return null;
35606     },
35607
35608     prevVisible : function(h){
35609         var v = this.view, cm = this.grid.colModel;
35610         h = h.prevSibling;
35611         while(h){
35612             if(!cm.isHidden(v.getCellIndex(h))){
35613                 return h;
35614             }
35615             h = h.prevSibling;
35616         }
35617         return null;
35618     },
35619
35620     positionIndicator : function(h, n, e){
35621         var x = Roo.lib.Event.getPageX(e);
35622         var r = Roo.lib.Dom.getRegion(n.firstChild);
35623         var px, pt, py = r.top + this.proxyOffsets[1];
35624         if((r.right - x) <= (r.right-r.left)/2){
35625             px = r.right+this.view.borderWidth;
35626             pt = "after";
35627         }else{
35628             px = r.left;
35629             pt = "before";
35630         }
35631         var oldIndex = this.view.getCellIndex(h);
35632         var newIndex = this.view.getCellIndex(n);
35633
35634         if(this.grid.colModel.isFixed(newIndex)){
35635             return false;
35636         }
35637
35638         var locked = this.grid.colModel.isLocked(newIndex);
35639
35640         if(pt == "after"){
35641             newIndex++;
35642         }
35643         if(oldIndex < newIndex){
35644             newIndex--;
35645         }
35646         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
35647             return false;
35648         }
35649         px +=  this.proxyOffsets[0];
35650         this.proxyTop.setLeftTop(px, py);
35651         this.proxyTop.show();
35652         if(!this.bottomOffset){
35653             this.bottomOffset = this.view.mainHd.getHeight();
35654         }
35655         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
35656         this.proxyBottom.show();
35657         return pt;
35658     },
35659
35660     onNodeEnter : function(n, dd, e, data){
35661         if(data.header != n){
35662             this.positionIndicator(data.header, n, e);
35663         }
35664     },
35665
35666     onNodeOver : function(n, dd, e, data){
35667         var result = false;
35668         if(data.header != n){
35669             result = this.positionIndicator(data.header, n, e);
35670         }
35671         if(!result){
35672             this.proxyTop.hide();
35673             this.proxyBottom.hide();
35674         }
35675         return result ? this.dropAllowed : this.dropNotAllowed;
35676     },
35677
35678     onNodeOut : function(n, dd, e, data){
35679         this.proxyTop.hide();
35680         this.proxyBottom.hide();
35681     },
35682
35683     onNodeDrop : function(n, dd, e, data){
35684         var h = data.header;
35685         if(h != n){
35686             var cm = this.grid.colModel;
35687             var x = Roo.lib.Event.getPageX(e);
35688             var r = Roo.lib.Dom.getRegion(n.firstChild);
35689             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
35690             var oldIndex = this.view.getCellIndex(h);
35691             var newIndex = this.view.getCellIndex(n);
35692             var locked = cm.isLocked(newIndex);
35693             if(pt == "after"){
35694                 newIndex++;
35695             }
35696             if(oldIndex < newIndex){
35697                 newIndex--;
35698             }
35699             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
35700                 return false;
35701             }
35702             cm.setLocked(oldIndex, locked, true);
35703             cm.moveColumn(oldIndex, newIndex);
35704             this.grid.fireEvent("columnmove", oldIndex, newIndex);
35705             return true;
35706         }
35707         return false;
35708     }
35709 });
35710 /*
35711  * Based on:
35712  * Ext JS Library 1.1.1
35713  * Copyright(c) 2006-2007, Ext JS, LLC.
35714  *
35715  * Originally Released Under LGPL - original licence link has changed is not relivant.
35716  *
35717  * Fork - LGPL
35718  * <script type="text/javascript">
35719  */
35720   
35721 /**
35722  * @class Roo.grid.GridView
35723  * @extends Roo.util.Observable
35724  *
35725  * @constructor
35726  * @param {Object} config
35727  */
35728 Roo.grid.GridView = function(config){
35729     Roo.grid.GridView.superclass.constructor.call(this);
35730     this.el = null;
35731
35732     Roo.apply(this, config);
35733 };
35734
35735 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
35736
35737     unselectable :  'unselectable="on"',
35738     unselectableCls :  'x-unselectable',
35739     
35740     
35741     rowClass : "x-grid-row",
35742
35743     cellClass : "x-grid-col",
35744
35745     tdClass : "x-grid-td",
35746
35747     hdClass : "x-grid-hd",
35748
35749     splitClass : "x-grid-split",
35750
35751     sortClasses : ["sort-asc", "sort-desc"],
35752
35753     enableMoveAnim : false,
35754
35755     hlColor: "C3DAF9",
35756
35757     dh : Roo.DomHelper,
35758
35759     fly : Roo.Element.fly,
35760
35761     css : Roo.util.CSS,
35762
35763     borderWidth: 1,
35764
35765     splitOffset: 3,
35766
35767     scrollIncrement : 22,
35768
35769     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
35770
35771     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
35772
35773     bind : function(ds, cm){
35774         if(this.ds){
35775             this.ds.un("load", this.onLoad, this);
35776             this.ds.un("datachanged", this.onDataChange, this);
35777             this.ds.un("add", this.onAdd, this);
35778             this.ds.un("remove", this.onRemove, this);
35779             this.ds.un("update", this.onUpdate, this);
35780             this.ds.un("clear", this.onClear, this);
35781         }
35782         if(ds){
35783             ds.on("load", this.onLoad, this);
35784             ds.on("datachanged", this.onDataChange, this);
35785             ds.on("add", this.onAdd, this);
35786             ds.on("remove", this.onRemove, this);
35787             ds.on("update", this.onUpdate, this);
35788             ds.on("clear", this.onClear, this);
35789         }
35790         this.ds = ds;
35791
35792         if(this.cm){
35793             this.cm.un("widthchange", this.onColWidthChange, this);
35794             this.cm.un("headerchange", this.onHeaderChange, this);
35795             this.cm.un("hiddenchange", this.onHiddenChange, this);
35796             this.cm.un("columnmoved", this.onColumnMove, this);
35797             this.cm.un("columnlockchange", this.onColumnLock, this);
35798         }
35799         if(cm){
35800             this.generateRules(cm);
35801             cm.on("widthchange", this.onColWidthChange, this);
35802             cm.on("headerchange", this.onHeaderChange, this);
35803             cm.on("hiddenchange", this.onHiddenChange, this);
35804             cm.on("columnmoved", this.onColumnMove, this);
35805             cm.on("columnlockchange", this.onColumnLock, this);
35806         }
35807         this.cm = cm;
35808     },
35809
35810     init: function(grid){
35811         Roo.grid.GridView.superclass.init.call(this, grid);
35812
35813         this.bind(grid.dataSource, grid.colModel);
35814
35815         grid.on("headerclick", this.handleHeaderClick, this);
35816
35817         if(grid.trackMouseOver){
35818             grid.on("mouseover", this.onRowOver, this);
35819             grid.on("mouseout", this.onRowOut, this);
35820         }
35821         grid.cancelTextSelection = function(){};
35822         this.gridId = grid.id;
35823
35824         var tpls = this.templates || {};
35825
35826         if(!tpls.master){
35827             tpls.master = new Roo.Template(
35828                '<div class="x-grid" hidefocus="true">',
35829                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
35830                   '<div class="x-grid-topbar"></div>',
35831                   '<div class="x-grid-scroller"><div></div></div>',
35832                   '<div class="x-grid-locked">',
35833                       '<div class="x-grid-header">{lockedHeader}</div>',
35834                       '<div class="x-grid-body">{lockedBody}</div>',
35835                   "</div>",
35836                   '<div class="x-grid-viewport">',
35837                       '<div class="x-grid-header">{header}</div>',
35838                       '<div class="x-grid-body">{body}</div>',
35839                   "</div>",
35840                   '<div class="x-grid-bottombar"></div>',
35841                  
35842                   '<div class="x-grid-resize-proxy">&#160;</div>',
35843                "</div>"
35844             );
35845             tpls.master.disableformats = true;
35846         }
35847
35848         if(!tpls.header){
35849             tpls.header = new Roo.Template(
35850                '<table border="0" cellspacing="0" cellpadding="0">',
35851                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
35852                "</table>{splits}"
35853             );
35854             tpls.header.disableformats = true;
35855         }
35856         tpls.header.compile();
35857
35858         if(!tpls.hcell){
35859             tpls.hcell = new Roo.Template(
35860                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
35861                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
35862                 "</div></td>"
35863              );
35864              tpls.hcell.disableFormats = true;
35865         }
35866         tpls.hcell.compile();
35867
35868         if(!tpls.hsplit){
35869             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
35870                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
35871             tpls.hsplit.disableFormats = true;
35872         }
35873         tpls.hsplit.compile();
35874
35875         if(!tpls.body){
35876             tpls.body = new Roo.Template(
35877                '<table border="0" cellspacing="0" cellpadding="0">',
35878                "<tbody>{rows}</tbody>",
35879                "</table>"
35880             );
35881             tpls.body.disableFormats = true;
35882         }
35883         tpls.body.compile();
35884
35885         if(!tpls.row){
35886             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
35887             tpls.row.disableFormats = true;
35888         }
35889         tpls.row.compile();
35890
35891         if(!tpls.cell){
35892             tpls.cell = new Roo.Template(
35893                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
35894                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
35895                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
35896                 "</td>"
35897             );
35898             tpls.cell.disableFormats = true;
35899         }
35900         tpls.cell.compile();
35901
35902         this.templates = tpls;
35903     },
35904
35905     // remap these for backwards compat
35906     onColWidthChange : function(){
35907         this.updateColumns.apply(this, arguments);
35908     },
35909     onHeaderChange : function(){
35910         this.updateHeaders.apply(this, arguments);
35911     }, 
35912     onHiddenChange : function(){
35913         this.handleHiddenChange.apply(this, arguments);
35914     },
35915     onColumnMove : function(){
35916         this.handleColumnMove.apply(this, arguments);
35917     },
35918     onColumnLock : function(){
35919         this.handleLockChange.apply(this, arguments);
35920     },
35921
35922     onDataChange : function(){
35923         this.refresh();
35924         this.updateHeaderSortState();
35925     },
35926
35927     onClear : function(){
35928         this.refresh();
35929     },
35930
35931     onUpdate : function(ds, record){
35932         this.refreshRow(record);
35933     },
35934
35935     refreshRow : function(record){
35936         var ds = this.ds, index;
35937         if(typeof record == 'number'){
35938             index = record;
35939             record = ds.getAt(index);
35940         }else{
35941             index = ds.indexOf(record);
35942         }
35943         this.insertRows(ds, index, index, true);
35944         this.onRemove(ds, record, index+1, true);
35945         this.syncRowHeights(index, index);
35946         this.layout();
35947         this.fireEvent("rowupdated", this, index, record);
35948     },
35949
35950     onAdd : function(ds, records, index){
35951         this.insertRows(ds, index, index + (records.length-1));
35952     },
35953
35954     onRemove : function(ds, record, index, isUpdate){
35955         if(isUpdate !== true){
35956             this.fireEvent("beforerowremoved", this, index, record);
35957         }
35958         var bt = this.getBodyTable(), lt = this.getLockedTable();
35959         if(bt.rows[index]){
35960             bt.firstChild.removeChild(bt.rows[index]);
35961         }
35962         if(lt.rows[index]){
35963             lt.firstChild.removeChild(lt.rows[index]);
35964         }
35965         if(isUpdate !== true){
35966             this.stripeRows(index);
35967             this.syncRowHeights(index, index);
35968             this.layout();
35969             this.fireEvent("rowremoved", this, index, record);
35970         }
35971     },
35972
35973     onLoad : function(){
35974         this.scrollToTop();
35975     },
35976
35977     /**
35978      * Scrolls the grid to the top
35979      */
35980     scrollToTop : function(){
35981         if(this.scroller){
35982             this.scroller.dom.scrollTop = 0;
35983             this.syncScroll();
35984         }
35985     },
35986
35987     /**
35988      * Gets a panel in the header of the grid that can be used for toolbars etc.
35989      * After modifying the contents of this panel a call to grid.autoSize() may be
35990      * required to register any changes in size.
35991      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
35992      * @return Roo.Element
35993      */
35994     getHeaderPanel : function(doShow){
35995         if(doShow){
35996             this.headerPanel.show();
35997         }
35998         return this.headerPanel;
35999     },
36000
36001     /**
36002      * Gets a panel in the footer of the grid that can be used for toolbars etc.
36003      * After modifying the contents of this panel a call to grid.autoSize() may be
36004      * required to register any changes in size.
36005      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
36006      * @return Roo.Element
36007      */
36008     getFooterPanel : function(doShow){
36009         if(doShow){
36010             this.footerPanel.show();
36011         }
36012         return this.footerPanel;
36013     },
36014
36015     initElements : function(){
36016         var E = Roo.Element;
36017         var el = this.grid.getGridEl().dom.firstChild;
36018         var cs = el.childNodes;
36019
36020         this.el = new E(el);
36021         
36022          this.focusEl = new E(el.firstChild);
36023         this.focusEl.swallowEvent("click", true);
36024         
36025         this.headerPanel = new E(cs[1]);
36026         this.headerPanel.enableDisplayMode("block");
36027
36028         this.scroller = new E(cs[2]);
36029         this.scrollSizer = new E(this.scroller.dom.firstChild);
36030
36031         this.lockedWrap = new E(cs[3]);
36032         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
36033         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
36034
36035         this.mainWrap = new E(cs[4]);
36036         this.mainHd = new E(this.mainWrap.dom.firstChild);
36037         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
36038
36039         this.footerPanel = new E(cs[5]);
36040         this.footerPanel.enableDisplayMode("block");
36041
36042         this.resizeProxy = new E(cs[6]);
36043
36044         this.headerSelector = String.format(
36045            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
36046            this.lockedHd.id, this.mainHd.id
36047         );
36048
36049         this.splitterSelector = String.format(
36050            '#{0} div.x-grid-split, #{1} div.x-grid-split',
36051            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
36052         );
36053     },
36054     idToCssName : function(s)
36055     {
36056         return s.replace(/[^a-z0-9]+/ig, '-');
36057     },
36058
36059     getHeaderCell : function(index){
36060         return Roo.DomQuery.select(this.headerSelector)[index];
36061     },
36062
36063     getHeaderCellMeasure : function(index){
36064         return this.getHeaderCell(index).firstChild;
36065     },
36066
36067     getHeaderCellText : function(index){
36068         return this.getHeaderCell(index).firstChild.firstChild;
36069     },
36070
36071     getLockedTable : function(){
36072         return this.lockedBody.dom.firstChild;
36073     },
36074
36075     getBodyTable : function(){
36076         return this.mainBody.dom.firstChild;
36077     },
36078
36079     getLockedRow : function(index){
36080         return this.getLockedTable().rows[index];
36081     },
36082
36083     getRow : function(index){
36084         return this.getBodyTable().rows[index];
36085     },
36086
36087     getRowComposite : function(index){
36088         if(!this.rowEl){
36089             this.rowEl = new Roo.CompositeElementLite();
36090         }
36091         var els = [], lrow, mrow;
36092         if(lrow = this.getLockedRow(index)){
36093             els.push(lrow);
36094         }
36095         if(mrow = this.getRow(index)){
36096             els.push(mrow);
36097         }
36098         this.rowEl.elements = els;
36099         return this.rowEl;
36100     },
36101     /**
36102      * Gets the 'td' of the cell
36103      * 
36104      * @param {Integer} rowIndex row to select
36105      * @param {Integer} colIndex column to select
36106      * 
36107      * @return {Object} 
36108      */
36109     getCell : function(rowIndex, colIndex){
36110         var locked = this.cm.getLockedCount();
36111         var source;
36112         if(colIndex < locked){
36113             source = this.lockedBody.dom.firstChild;
36114         }else{
36115             source = this.mainBody.dom.firstChild;
36116             colIndex -= locked;
36117         }
36118         return source.rows[rowIndex].childNodes[colIndex];
36119     },
36120
36121     getCellText : function(rowIndex, colIndex){
36122         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
36123     },
36124
36125     getCellBox : function(cell){
36126         var b = this.fly(cell).getBox();
36127         if(Roo.isOpera){ // opera fails to report the Y
36128             b.y = cell.offsetTop + this.mainBody.getY();
36129         }
36130         return b;
36131     },
36132
36133     getCellIndex : function(cell){
36134         var id = String(cell.className).match(this.cellRE);
36135         if(id){
36136             return parseInt(id[1], 10);
36137         }
36138         return 0;
36139     },
36140
36141     findHeaderIndex : function(n){
36142         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
36143         return r ? this.getCellIndex(r) : false;
36144     },
36145
36146     findHeaderCell : function(n){
36147         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
36148         return r ? r : false;
36149     },
36150
36151     findRowIndex : function(n){
36152         if(!n){
36153             return false;
36154         }
36155         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
36156         return r ? r.rowIndex : false;
36157     },
36158
36159     findCellIndex : function(node){
36160         var stop = this.el.dom;
36161         while(node && node != stop){
36162             if(this.findRE.test(node.className)){
36163                 return this.getCellIndex(node);
36164             }
36165             node = node.parentNode;
36166         }
36167         return false;
36168     },
36169
36170     getColumnId : function(index){
36171         return this.cm.getColumnId(index);
36172     },
36173
36174     getSplitters : function()
36175     {
36176         if(this.splitterSelector){
36177            return Roo.DomQuery.select(this.splitterSelector);
36178         }else{
36179             return null;
36180       }
36181     },
36182
36183     getSplitter : function(index){
36184         return this.getSplitters()[index];
36185     },
36186
36187     onRowOver : function(e, t){
36188         var row;
36189         if((row = this.findRowIndex(t)) !== false){
36190             this.getRowComposite(row).addClass("x-grid-row-over");
36191         }
36192     },
36193
36194     onRowOut : function(e, t){
36195         var row;
36196         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
36197             this.getRowComposite(row).removeClass("x-grid-row-over");
36198         }
36199     },
36200
36201     renderHeaders : function(){
36202         var cm = this.cm;
36203         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
36204         var cb = [], lb = [], sb = [], lsb = [], p = {};
36205         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
36206             p.cellId = "x-grid-hd-0-" + i;
36207             p.splitId = "x-grid-csplit-0-" + i;
36208             p.id = cm.getColumnId(i);
36209             p.title = cm.getColumnTooltip(i) || "";
36210             p.value = cm.getColumnHeader(i) || "";
36211             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
36212             if(!cm.isLocked(i)){
36213                 cb[cb.length] = ct.apply(p);
36214                 sb[sb.length] = st.apply(p);
36215             }else{
36216                 lb[lb.length] = ct.apply(p);
36217                 lsb[lsb.length] = st.apply(p);
36218             }
36219         }
36220         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
36221                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
36222     },
36223
36224     updateHeaders : function(){
36225         var html = this.renderHeaders();
36226         this.lockedHd.update(html[0]);
36227         this.mainHd.update(html[1]);
36228     },
36229
36230     /**
36231      * Focuses the specified row.
36232      * @param {Number} row The row index
36233      */
36234     focusRow : function(row)
36235     {
36236         //Roo.log('GridView.focusRow');
36237         var x = this.scroller.dom.scrollLeft;
36238         this.focusCell(row, 0, false);
36239         this.scroller.dom.scrollLeft = x;
36240     },
36241
36242     /**
36243      * Focuses the specified cell.
36244      * @param {Number} row The row index
36245      * @param {Number} col The column index
36246      * @param {Boolean} hscroll false to disable horizontal scrolling
36247      */
36248     focusCell : function(row, col, hscroll)
36249     {
36250         //Roo.log('GridView.focusCell');
36251         var el = this.ensureVisible(row, col, hscroll);
36252         this.focusEl.alignTo(el, "tl-tl");
36253         if(Roo.isGecko){
36254             this.focusEl.focus();
36255         }else{
36256             this.focusEl.focus.defer(1, this.focusEl);
36257         }
36258     },
36259
36260     /**
36261      * Scrolls the specified cell into view
36262      * @param {Number} row The row index
36263      * @param {Number} col The column index
36264      * @param {Boolean} hscroll false to disable horizontal scrolling
36265      */
36266     ensureVisible : function(row, col, hscroll)
36267     {
36268         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
36269         //return null; //disable for testing.
36270         if(typeof row != "number"){
36271             row = row.rowIndex;
36272         }
36273         if(row < 0 && row >= this.ds.getCount()){
36274             return  null;
36275         }
36276         col = (col !== undefined ? col : 0);
36277         var cm = this.grid.colModel;
36278         while(cm.isHidden(col)){
36279             col++;
36280         }
36281
36282         var el = this.getCell(row, col);
36283         if(!el){
36284             return null;
36285         }
36286         var c = this.scroller.dom;
36287
36288         var ctop = parseInt(el.offsetTop, 10);
36289         var cleft = parseInt(el.offsetLeft, 10);
36290         var cbot = ctop + el.offsetHeight;
36291         var cright = cleft + el.offsetWidth;
36292         
36293         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
36294         var stop = parseInt(c.scrollTop, 10);
36295         var sleft = parseInt(c.scrollLeft, 10);
36296         var sbot = stop + ch;
36297         var sright = sleft + c.clientWidth;
36298         /*
36299         Roo.log('GridView.ensureVisible:' +
36300                 ' ctop:' + ctop +
36301                 ' c.clientHeight:' + c.clientHeight +
36302                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
36303                 ' stop:' + stop +
36304                 ' cbot:' + cbot +
36305                 ' sbot:' + sbot +
36306                 ' ch:' + ch  
36307                 );
36308         */
36309         if(ctop < stop){
36310              c.scrollTop = ctop;
36311             //Roo.log("set scrolltop to ctop DISABLE?");
36312         }else if(cbot > sbot){
36313             //Roo.log("set scrolltop to cbot-ch");
36314             c.scrollTop = cbot-ch;
36315         }
36316         
36317         if(hscroll !== false){
36318             if(cleft < sleft){
36319                 c.scrollLeft = cleft;
36320             }else if(cright > sright){
36321                 c.scrollLeft = cright-c.clientWidth;
36322             }
36323         }
36324          
36325         return el;
36326     },
36327
36328     updateColumns : function(){
36329         this.grid.stopEditing();
36330         var cm = this.grid.colModel, colIds = this.getColumnIds();
36331         //var totalWidth = cm.getTotalWidth();
36332         var pos = 0;
36333         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
36334             //if(cm.isHidden(i)) continue;
36335             var w = cm.getColumnWidth(i);
36336             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
36337             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
36338         }
36339         this.updateSplitters();
36340     },
36341
36342     generateRules : function(cm){
36343         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
36344         Roo.util.CSS.removeStyleSheet(rulesId);
36345         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
36346             var cid = cm.getColumnId(i);
36347             var align = '';
36348             if(cm.config[i].align){
36349                 align = 'text-align:'+cm.config[i].align+';';
36350             }
36351             var hidden = '';
36352             if(cm.isHidden(i)){
36353                 hidden = 'display:none;';
36354             }
36355             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
36356             ruleBuf.push(
36357                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
36358                     this.hdSelector, cid, " {\n", align, width, "}\n",
36359                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
36360                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
36361         }
36362         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
36363     },
36364
36365     updateSplitters : function(){
36366         var cm = this.cm, s = this.getSplitters();
36367         if(s){ // splitters not created yet
36368             var pos = 0, locked = true;
36369             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
36370                 if(cm.isHidden(i)) continue;
36371                 var w = cm.getColumnWidth(i); // make sure it's a number
36372                 if(!cm.isLocked(i) && locked){
36373                     pos = 0;
36374                     locked = false;
36375                 }
36376                 pos += w;
36377                 s[i].style.left = (pos-this.splitOffset) + "px";
36378             }
36379         }
36380     },
36381
36382     handleHiddenChange : function(colModel, colIndex, hidden){
36383         if(hidden){
36384             this.hideColumn(colIndex);
36385         }else{
36386             this.unhideColumn(colIndex);
36387         }
36388     },
36389
36390     hideColumn : function(colIndex){
36391         var cid = this.getColumnId(colIndex);
36392         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
36393         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
36394         if(Roo.isSafari){
36395             this.updateHeaders();
36396         }
36397         this.updateSplitters();
36398         this.layout();
36399     },
36400
36401     unhideColumn : function(colIndex){
36402         var cid = this.getColumnId(colIndex);
36403         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
36404         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
36405
36406         if(Roo.isSafari){
36407             this.updateHeaders();
36408         }
36409         this.updateSplitters();
36410         this.layout();
36411     },
36412
36413     insertRows : function(dm, firstRow, lastRow, isUpdate){
36414         if(firstRow == 0 && lastRow == dm.getCount()-1){
36415             this.refresh();
36416         }else{
36417             if(!isUpdate){
36418                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
36419             }
36420             var s = this.getScrollState();
36421             var markup = this.renderRows(firstRow, lastRow);
36422             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
36423             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
36424             this.restoreScroll(s);
36425             if(!isUpdate){
36426                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
36427                 this.syncRowHeights(firstRow, lastRow);
36428                 this.stripeRows(firstRow);
36429                 this.layout();
36430             }
36431         }
36432     },
36433
36434     bufferRows : function(markup, target, index){
36435         var before = null, trows = target.rows, tbody = target.tBodies[0];
36436         if(index < trows.length){
36437             before = trows[index];
36438         }
36439         var b = document.createElement("div");
36440         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
36441         var rows = b.firstChild.rows;
36442         for(var i = 0, len = rows.length; i < len; i++){
36443             if(before){
36444                 tbody.insertBefore(rows[0], before);
36445             }else{
36446                 tbody.appendChild(rows[0]);
36447             }
36448         }
36449         b.innerHTML = "";
36450         b = null;
36451     },
36452
36453     deleteRows : function(dm, firstRow, lastRow){
36454         if(dm.getRowCount()<1){
36455             this.fireEvent("beforerefresh", this);
36456             this.mainBody.update("");
36457             this.lockedBody.update("");
36458             this.fireEvent("refresh", this);
36459         }else{
36460             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
36461             var bt = this.getBodyTable();
36462             var tbody = bt.firstChild;
36463             var rows = bt.rows;
36464             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
36465                 tbody.removeChild(rows[firstRow]);
36466             }
36467             this.stripeRows(firstRow);
36468             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
36469         }
36470     },
36471
36472     updateRows : function(dataSource, firstRow, lastRow){
36473         var s = this.getScrollState();
36474         this.refresh();
36475         this.restoreScroll(s);
36476     },
36477
36478     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
36479         if(!noRefresh){
36480            this.refresh();
36481         }
36482         this.updateHeaderSortState();
36483     },
36484
36485     getScrollState : function(){
36486         
36487         var sb = this.scroller.dom;
36488         return {left: sb.scrollLeft, top: sb.scrollTop};
36489     },
36490
36491     stripeRows : function(startRow){
36492         if(!this.grid.stripeRows || this.ds.getCount() < 1){
36493             return;
36494         }
36495         startRow = startRow || 0;
36496         var rows = this.getBodyTable().rows;
36497         var lrows = this.getLockedTable().rows;
36498         var cls = ' x-grid-row-alt ';
36499         for(var i = startRow, len = rows.length; i < len; i++){
36500             var row = rows[i], lrow = lrows[i];
36501             var isAlt = ((i+1) % 2 == 0);
36502             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
36503             if(isAlt == hasAlt){
36504                 continue;
36505             }
36506             if(isAlt){
36507                 row.className += " x-grid-row-alt";
36508             }else{
36509                 row.className = row.className.replace("x-grid-row-alt", "");
36510             }
36511             if(lrow){
36512                 lrow.className = row.className;
36513             }
36514         }
36515     },
36516
36517     restoreScroll : function(state){
36518         //Roo.log('GridView.restoreScroll');
36519         var sb = this.scroller.dom;
36520         sb.scrollLeft = state.left;
36521         sb.scrollTop = state.top;
36522         this.syncScroll();
36523     },
36524
36525     syncScroll : function(){
36526         //Roo.log('GridView.syncScroll');
36527         var sb = this.scroller.dom;
36528         var sh = this.mainHd.dom;
36529         var bs = this.mainBody.dom;
36530         var lv = this.lockedBody.dom;
36531         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
36532         lv.scrollTop = bs.scrollTop = sb.scrollTop;
36533     },
36534
36535     handleScroll : function(e){
36536         this.syncScroll();
36537         var sb = this.scroller.dom;
36538         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
36539         e.stopEvent();
36540     },
36541
36542     handleWheel : function(e){
36543         var d = e.getWheelDelta();
36544         this.scroller.dom.scrollTop -= d*22;
36545         // set this here to prevent jumpy scrolling on large tables
36546         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
36547         e.stopEvent();
36548     },
36549
36550     renderRows : function(startRow, endRow){
36551         // pull in all the crap needed to render rows
36552         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
36553         var colCount = cm.getColumnCount();
36554
36555         if(ds.getCount() < 1){
36556             return ["", ""];
36557         }
36558
36559         // build a map for all the columns
36560         var cs = [];
36561         for(var i = 0; i < colCount; i++){
36562             var name = cm.getDataIndex(i);
36563             cs[i] = {
36564                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
36565                 renderer : cm.getRenderer(i),
36566                 id : cm.getColumnId(i),
36567                 locked : cm.isLocked(i)
36568             };
36569         }
36570
36571         startRow = startRow || 0;
36572         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
36573
36574         // records to render
36575         var rs = ds.getRange(startRow, endRow);
36576
36577         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
36578     },
36579
36580     // As much as I hate to duplicate code, this was branched because FireFox really hates
36581     // [].join("") on strings. The performance difference was substantial enough to
36582     // branch this function
36583     doRender : Roo.isGecko ?
36584             function(cs, rs, ds, startRow, colCount, stripe){
36585                 var ts = this.templates, ct = ts.cell, rt = ts.row;
36586                 // buffers
36587                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
36588                 
36589                 var hasListener = this.grid.hasListener('rowclass');
36590                 var rowcfg = {};
36591                 for(var j = 0, len = rs.length; j < len; j++){
36592                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
36593                     for(var i = 0; i < colCount; i++){
36594                         c = cs[i];
36595                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
36596                         p.id = c.id;
36597                         p.css = p.attr = "";
36598                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
36599                         if(p.value == undefined || p.value === "") p.value = "&#160;";
36600                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
36601                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
36602                         }
36603                         var markup = ct.apply(p);
36604                         if(!c.locked){
36605                             cb+= markup;
36606                         }else{
36607                             lcb+= markup;
36608                         }
36609                     }
36610                     var alt = [];
36611                     if(stripe && ((rowIndex+1) % 2 == 0)){
36612                         alt.push("x-grid-row-alt")
36613                     }
36614                     if(r.dirty){
36615                         alt.push(  " x-grid-dirty-row");
36616                     }
36617                     rp.cells = lcb;
36618                     if(this.getRowClass){
36619                         alt.push(this.getRowClass(r, rowIndex));
36620                     }
36621                     if (hasListener) {
36622                         rowcfg = {
36623                              
36624                             record: r,
36625                             rowIndex : rowIndex,
36626                             rowClass : ''
36627                         }
36628                         this.grid.fireEvent('rowclass', this, rowcfg);
36629                         alt.push(rowcfg.rowClass);
36630                     }
36631                     rp.alt = alt.join(" ");
36632                     lbuf+= rt.apply(rp);
36633                     rp.cells = cb;
36634                     buf+=  rt.apply(rp);
36635                 }
36636                 return [lbuf, buf];
36637             } :
36638             function(cs, rs, ds, startRow, colCount, stripe){
36639                 var ts = this.templates, ct = ts.cell, rt = ts.row;
36640                 // buffers
36641                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
36642                 var hasListener = this.grid.hasListener('rowclass');
36643  
36644                 var rowcfg = {};
36645                 for(var j = 0, len = rs.length; j < len; j++){
36646                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
36647                     for(var i = 0; i < colCount; i++){
36648                         c = cs[i];
36649                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
36650                         p.id = c.id;
36651                         p.css = p.attr = "";
36652                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
36653                         if(p.value == undefined || p.value === "") p.value = "&#160;";
36654                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
36655                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
36656                         }
36657                         
36658                         var markup = ct.apply(p);
36659                         if(!c.locked){
36660                             cb[cb.length] = markup;
36661                         }else{
36662                             lcb[lcb.length] = markup;
36663                         }
36664                     }
36665                     var alt = [];
36666                     if(stripe && ((rowIndex+1) % 2 == 0)){
36667                         alt.push( "x-grid-row-alt");
36668                     }
36669                     if(r.dirty){
36670                         alt.push(" x-grid-dirty-row");
36671                     }
36672                     rp.cells = lcb;
36673                     if(this.getRowClass){
36674                         alt.push( this.getRowClass(r, rowIndex));
36675                     }
36676                     if (hasListener) {
36677                         rowcfg = {
36678                              
36679                             record: r,
36680                             rowIndex : rowIndex,
36681                             rowClass : ''
36682                         }
36683                         this.grid.fireEvent('rowclass', this, rowcfg);
36684                         alt.push(rowcfg.rowClass);
36685                     }
36686                     rp.alt = alt.join(" ");
36687                     rp.cells = lcb.join("");
36688                     lbuf[lbuf.length] = rt.apply(rp);
36689                     rp.cells = cb.join("");
36690                     buf[buf.length] =  rt.apply(rp);
36691                 }
36692                 return [lbuf.join(""), buf.join("")];
36693             },
36694
36695     renderBody : function(){
36696         var markup = this.renderRows();
36697         var bt = this.templates.body;
36698         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
36699     },
36700
36701     /**
36702      * Refreshes the grid
36703      * @param {Boolean} headersToo
36704      */
36705     refresh : function(headersToo){
36706         this.fireEvent("beforerefresh", this);
36707         this.grid.stopEditing();
36708         var result = this.renderBody();
36709         this.lockedBody.update(result[0]);
36710         this.mainBody.update(result[1]);
36711         if(headersToo === true){
36712             this.updateHeaders();
36713             this.updateColumns();
36714             this.updateSplitters();
36715             this.updateHeaderSortState();
36716         }
36717         this.syncRowHeights();
36718         this.layout();
36719         this.fireEvent("refresh", this);
36720     },
36721
36722     handleColumnMove : function(cm, oldIndex, newIndex){
36723         this.indexMap = null;
36724         var s = this.getScrollState();
36725         this.refresh(true);
36726         this.restoreScroll(s);
36727         this.afterMove(newIndex);
36728     },
36729
36730     afterMove : function(colIndex){
36731         if(this.enableMoveAnim && Roo.enableFx){
36732             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
36733         }
36734         // if multisort - fix sortOrder, and reload..
36735         if (this.grid.dataSource.multiSort) {
36736             // the we can call sort again..
36737             var dm = this.grid.dataSource;
36738             var cm = this.grid.colModel;
36739             var so = [];
36740             for(var i = 0; i < cm.config.length; i++ ) {
36741                 
36742                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
36743                     continue; // dont' bother, it's not in sort list or being set.
36744                 }
36745                 
36746                 so.push(cm.config[i].dataIndex);
36747             };
36748             dm.sortOrder = so;
36749             dm.load(dm.lastOptions);
36750             
36751             
36752         }
36753         
36754     },
36755
36756     updateCell : function(dm, rowIndex, dataIndex){
36757         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
36758         if(typeof colIndex == "undefined"){ // not present in grid
36759             return;
36760         }
36761         var cm = this.grid.colModel;
36762         var cell = this.getCell(rowIndex, colIndex);
36763         var cellText = this.getCellText(rowIndex, colIndex);
36764
36765         var p = {
36766             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
36767             id : cm.getColumnId(colIndex),
36768             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
36769         };
36770         var renderer = cm.getRenderer(colIndex);
36771         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
36772         if(typeof val == "undefined" || val === "") val = "&#160;";
36773         cellText.innerHTML = val;
36774         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
36775         this.syncRowHeights(rowIndex, rowIndex);
36776     },
36777
36778     calcColumnWidth : function(colIndex, maxRowsToMeasure){
36779         var maxWidth = 0;
36780         if(this.grid.autoSizeHeaders){
36781             var h = this.getHeaderCellMeasure(colIndex);
36782             maxWidth = Math.max(maxWidth, h.scrollWidth);
36783         }
36784         var tb, index;
36785         if(this.cm.isLocked(colIndex)){
36786             tb = this.getLockedTable();
36787             index = colIndex;
36788         }else{
36789             tb = this.getBodyTable();
36790             index = colIndex - this.cm.getLockedCount();
36791         }
36792         if(tb && tb.rows){
36793             var rows = tb.rows;
36794             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
36795             for(var i = 0; i < stopIndex; i++){
36796                 var cell = rows[i].childNodes[index].firstChild;
36797                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
36798             }
36799         }
36800         return maxWidth + /*margin for error in IE*/ 5;
36801     },
36802     /**
36803      * Autofit a column to its content.
36804      * @param {Number} colIndex
36805      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
36806      */
36807      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
36808          if(this.cm.isHidden(colIndex)){
36809              return; // can't calc a hidden column
36810          }
36811         if(forceMinSize){
36812             var cid = this.cm.getColumnId(colIndex);
36813             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
36814            if(this.grid.autoSizeHeaders){
36815                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
36816            }
36817         }
36818         var newWidth = this.calcColumnWidth(colIndex);
36819         this.cm.setColumnWidth(colIndex,
36820             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
36821         if(!suppressEvent){
36822             this.grid.fireEvent("columnresize", colIndex, newWidth);
36823         }
36824     },
36825
36826     /**
36827      * Autofits all columns to their content and then expands to fit any extra space in the grid
36828      */
36829      autoSizeColumns : function(){
36830         var cm = this.grid.colModel;
36831         var colCount = cm.getColumnCount();
36832         for(var i = 0; i < colCount; i++){
36833             this.autoSizeColumn(i, true, true);
36834         }
36835         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
36836             this.fitColumns();
36837         }else{
36838             this.updateColumns();
36839             this.layout();
36840         }
36841     },
36842
36843     /**
36844      * Autofits all columns to the grid's width proportionate with their current size
36845      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
36846      */
36847     fitColumns : function(reserveScrollSpace){
36848         var cm = this.grid.colModel;
36849         var colCount = cm.getColumnCount();
36850         var cols = [];
36851         var width = 0;
36852         var i, w;
36853         for (i = 0; i < colCount; i++){
36854             if(!cm.isHidden(i) && !cm.isFixed(i)){
36855                 w = cm.getColumnWidth(i);
36856                 cols.push(i);
36857                 cols.push(w);
36858                 width += w;
36859             }
36860         }
36861         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
36862         if(reserveScrollSpace){
36863             avail -= 17;
36864         }
36865         var frac = (avail - cm.getTotalWidth())/width;
36866         while (cols.length){
36867             w = cols.pop();
36868             i = cols.pop();
36869             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
36870         }
36871         this.updateColumns();
36872         this.layout();
36873     },
36874
36875     onRowSelect : function(rowIndex){
36876         var row = this.getRowComposite(rowIndex);
36877         row.addClass("x-grid-row-selected");
36878     },
36879
36880     onRowDeselect : function(rowIndex){
36881         var row = this.getRowComposite(rowIndex);
36882         row.removeClass("x-grid-row-selected");
36883     },
36884
36885     onCellSelect : function(row, col){
36886         var cell = this.getCell(row, col);
36887         if(cell){
36888             Roo.fly(cell).addClass("x-grid-cell-selected");
36889         }
36890     },
36891
36892     onCellDeselect : function(row, col){
36893         var cell = this.getCell(row, col);
36894         if(cell){
36895             Roo.fly(cell).removeClass("x-grid-cell-selected");
36896         }
36897     },
36898
36899     updateHeaderSortState : function(){
36900         
36901         // sort state can be single { field: xxx, direction : yyy}
36902         // or   { xxx=>ASC , yyy : DESC ..... }
36903         
36904         var mstate = {};
36905         if (!this.ds.multiSort) { 
36906             var state = this.ds.getSortState();
36907             if(!state){
36908                 return;
36909             }
36910             mstate[state.field] = state.direction;
36911             // FIXME... - this is not used here.. but might be elsewhere..
36912             this.sortState = state;
36913             
36914         } else {
36915             mstate = this.ds.sortToggle;
36916         }
36917         //remove existing sort classes..
36918         
36919         var sc = this.sortClasses;
36920         var hds = this.el.select(this.headerSelector).removeClass(sc);
36921         
36922         for(var f in mstate) {
36923         
36924             var sortColumn = this.cm.findColumnIndex(f);
36925             
36926             if(sortColumn != -1){
36927                 var sortDir = mstate[f];        
36928                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
36929             }
36930         }
36931         
36932          
36933         
36934     },
36935
36936
36937     handleHeaderClick : function(g, index){
36938         if(this.headersDisabled){
36939             return;
36940         }
36941         var dm = g.dataSource, cm = g.colModel;
36942         if(!cm.isSortable(index)){
36943             return;
36944         }
36945         g.stopEditing();
36946         
36947         if (dm.multiSort) {
36948             // update the sortOrder
36949             var so = [];
36950             for(var i = 0; i < cm.config.length; i++ ) {
36951                 
36952                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
36953                     continue; // dont' bother, it's not in sort list or being set.
36954                 }
36955                 
36956                 so.push(cm.config[i].dataIndex);
36957             };
36958             dm.sortOrder = so;
36959         }
36960         
36961         
36962         dm.sort(cm.getDataIndex(index));
36963     },
36964
36965
36966     destroy : function(){
36967         if(this.colMenu){
36968             this.colMenu.removeAll();
36969             Roo.menu.MenuMgr.unregister(this.colMenu);
36970             this.colMenu.getEl().remove();
36971             delete this.colMenu;
36972         }
36973         if(this.hmenu){
36974             this.hmenu.removeAll();
36975             Roo.menu.MenuMgr.unregister(this.hmenu);
36976             this.hmenu.getEl().remove();
36977             delete this.hmenu;
36978         }
36979         if(this.grid.enableColumnMove){
36980             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
36981             if(dds){
36982                 for(var dd in dds){
36983                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
36984                         var elid = dds[dd].dragElId;
36985                         dds[dd].unreg();
36986                         Roo.get(elid).remove();
36987                     } else if(dds[dd].config.isTarget){
36988                         dds[dd].proxyTop.remove();
36989                         dds[dd].proxyBottom.remove();
36990                         dds[dd].unreg();
36991                     }
36992                     if(Roo.dd.DDM.locationCache[dd]){
36993                         delete Roo.dd.DDM.locationCache[dd];
36994                     }
36995                 }
36996                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
36997             }
36998         }
36999         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
37000         this.bind(null, null);
37001         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
37002     },
37003
37004     handleLockChange : function(){
37005         this.refresh(true);
37006     },
37007
37008     onDenyColumnLock : function(){
37009
37010     },
37011
37012     onDenyColumnHide : function(){
37013
37014     },
37015
37016     handleHdMenuClick : function(item){
37017         var index = this.hdCtxIndex;
37018         var cm = this.cm, ds = this.ds;
37019         switch(item.id){
37020             case "asc":
37021                 ds.sort(cm.getDataIndex(index), "ASC");
37022                 break;
37023             case "desc":
37024                 ds.sort(cm.getDataIndex(index), "DESC");
37025                 break;
37026             case "lock":
37027                 var lc = cm.getLockedCount();
37028                 if(cm.getColumnCount(true) <= lc+1){
37029                     this.onDenyColumnLock();
37030                     return;
37031                 }
37032                 if(lc != index){
37033                     cm.setLocked(index, true, true);
37034                     cm.moveColumn(index, lc);
37035                     this.grid.fireEvent("columnmove", index, lc);
37036                 }else{
37037                     cm.setLocked(index, true);
37038                 }
37039             break;
37040             case "unlock":
37041                 var lc = cm.getLockedCount();
37042                 if((lc-1) != index){
37043                     cm.setLocked(index, false, true);
37044                     cm.moveColumn(index, lc-1);
37045                     this.grid.fireEvent("columnmove", index, lc-1);
37046                 }else{
37047                     cm.setLocked(index, false);
37048                 }
37049             break;
37050             default:
37051                 index = cm.getIndexById(item.id.substr(4));
37052                 if(index != -1){
37053                     if(item.checked && cm.getColumnCount(true) <= 1){
37054                         this.onDenyColumnHide();
37055                         return false;
37056                     }
37057                     cm.setHidden(index, item.checked);
37058                 }
37059         }
37060         return true;
37061     },
37062
37063     beforeColMenuShow : function(){
37064         var cm = this.cm,  colCount = cm.getColumnCount();
37065         this.colMenu.removeAll();
37066         for(var i = 0; i < colCount; i++){
37067             this.colMenu.add(new Roo.menu.CheckItem({
37068                 id: "col-"+cm.getColumnId(i),
37069                 text: cm.getColumnHeader(i),
37070                 checked: !cm.isHidden(i),
37071                 hideOnClick:false
37072             }));
37073         }
37074     },
37075
37076     handleHdCtx : function(g, index, e){
37077         e.stopEvent();
37078         var hd = this.getHeaderCell(index);
37079         this.hdCtxIndex = index;
37080         var ms = this.hmenu.items, cm = this.cm;
37081         ms.get("asc").setDisabled(!cm.isSortable(index));
37082         ms.get("desc").setDisabled(!cm.isSortable(index));
37083         if(this.grid.enableColLock !== false){
37084             ms.get("lock").setDisabled(cm.isLocked(index));
37085             ms.get("unlock").setDisabled(!cm.isLocked(index));
37086         }
37087         this.hmenu.show(hd, "tl-bl");
37088     },
37089
37090     handleHdOver : function(e){
37091         var hd = this.findHeaderCell(e.getTarget());
37092         if(hd && !this.headersDisabled){
37093             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
37094                this.fly(hd).addClass("x-grid-hd-over");
37095             }
37096         }
37097     },
37098
37099     handleHdOut : function(e){
37100         var hd = this.findHeaderCell(e.getTarget());
37101         if(hd){
37102             this.fly(hd).removeClass("x-grid-hd-over");
37103         }
37104     },
37105
37106     handleSplitDblClick : function(e, t){
37107         var i = this.getCellIndex(t);
37108         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
37109             this.autoSizeColumn(i, true);
37110             this.layout();
37111         }
37112     },
37113
37114     render : function(){
37115
37116         var cm = this.cm;
37117         var colCount = cm.getColumnCount();
37118
37119         if(this.grid.monitorWindowResize === true){
37120             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37121         }
37122         var header = this.renderHeaders();
37123         var body = this.templates.body.apply({rows:""});
37124         var html = this.templates.master.apply({
37125             lockedBody: body,
37126             body: body,
37127             lockedHeader: header[0],
37128             header: header[1]
37129         });
37130
37131         //this.updateColumns();
37132
37133         this.grid.getGridEl().dom.innerHTML = html;
37134
37135         this.initElements();
37136         
37137         // a kludge to fix the random scolling effect in webkit
37138         this.el.on("scroll", function() {
37139             this.el.dom.scrollTop=0; // hopefully not recursive..
37140         },this);
37141
37142         this.scroller.on("scroll", this.handleScroll, this);
37143         this.lockedBody.on("mousewheel", this.handleWheel, this);
37144         this.mainBody.on("mousewheel", this.handleWheel, this);
37145
37146         this.mainHd.on("mouseover", this.handleHdOver, this);
37147         this.mainHd.on("mouseout", this.handleHdOut, this);
37148         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
37149                 {delegate: "."+this.splitClass});
37150
37151         this.lockedHd.on("mouseover", this.handleHdOver, this);
37152         this.lockedHd.on("mouseout", this.handleHdOut, this);
37153         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
37154                 {delegate: "."+this.splitClass});
37155
37156         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
37157             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
37158         }
37159
37160         this.updateSplitters();
37161
37162         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
37163             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
37164             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
37165         }
37166
37167         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
37168             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
37169             this.hmenu.add(
37170                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
37171                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
37172             );
37173             if(this.grid.enableColLock !== false){
37174                 this.hmenu.add('-',
37175                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
37176                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
37177                 );
37178             }
37179             if(this.grid.enableColumnHide !== false){
37180
37181                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
37182                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
37183                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
37184
37185                 this.hmenu.add('-',
37186                     {id:"columns", text: this.columnsText, menu: this.colMenu}
37187                 );
37188             }
37189             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
37190
37191             this.grid.on("headercontextmenu", this.handleHdCtx, this);
37192         }
37193
37194         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
37195             this.dd = new Roo.grid.GridDragZone(this.grid, {
37196                 ddGroup : this.grid.ddGroup || 'GridDD'
37197             });
37198             
37199         }
37200
37201         /*
37202         for(var i = 0; i < colCount; i++){
37203             if(cm.isHidden(i)){
37204                 this.hideColumn(i);
37205             }
37206             if(cm.config[i].align){
37207                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
37208                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
37209             }
37210         }*/
37211         
37212         this.updateHeaderSortState();
37213
37214         this.beforeInitialResize();
37215         this.layout(true);
37216
37217         // two part rendering gives faster view to the user
37218         this.renderPhase2.defer(1, this);
37219     },
37220
37221     renderPhase2 : function(){
37222         // render the rows now
37223         this.refresh();
37224         if(this.grid.autoSizeColumns){
37225             this.autoSizeColumns();
37226         }
37227     },
37228
37229     beforeInitialResize : function(){
37230
37231     },
37232
37233     onColumnSplitterMoved : function(i, w){
37234         this.userResized = true;
37235         var cm = this.grid.colModel;
37236         cm.setColumnWidth(i, w, true);
37237         var cid = cm.getColumnId(i);
37238         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
37239         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
37240         this.updateSplitters();
37241         this.layout();
37242         this.grid.fireEvent("columnresize", i, w);
37243     },
37244
37245     syncRowHeights : function(startIndex, endIndex){
37246         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
37247             startIndex = startIndex || 0;
37248             var mrows = this.getBodyTable().rows;
37249             var lrows = this.getLockedTable().rows;
37250             var len = mrows.length-1;
37251             endIndex = Math.min(endIndex || len, len);
37252             for(var i = startIndex; i <= endIndex; i++){
37253                 var m = mrows[i], l = lrows[i];
37254                 var h = Math.max(m.offsetHeight, l.offsetHeight);
37255                 m.style.height = l.style.height = h + "px";
37256             }
37257         }
37258     },
37259
37260     layout : function(initialRender, is2ndPass){
37261         var g = this.grid;
37262         var auto = g.autoHeight;
37263         var scrollOffset = 16;
37264         var c = g.getGridEl(), cm = this.cm,
37265                 expandCol = g.autoExpandColumn,
37266                 gv = this;
37267         //c.beginMeasure();
37268
37269         if(!c.dom.offsetWidth){ // display:none?
37270             if(initialRender){
37271                 this.lockedWrap.show();
37272                 this.mainWrap.show();
37273             }
37274             return;
37275         }
37276
37277         var hasLock = this.cm.isLocked(0);
37278
37279         var tbh = this.headerPanel.getHeight();
37280         var bbh = this.footerPanel.getHeight();
37281
37282         if(auto){
37283             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
37284             var newHeight = ch + c.getBorderWidth("tb");
37285             if(g.maxHeight){
37286                 newHeight = Math.min(g.maxHeight, newHeight);
37287             }
37288             c.setHeight(newHeight);
37289         }
37290
37291         if(g.autoWidth){
37292             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
37293         }
37294
37295         var s = this.scroller;
37296
37297         var csize = c.getSize(true);
37298
37299         this.el.setSize(csize.width, csize.height);
37300
37301         this.headerPanel.setWidth(csize.width);
37302         this.footerPanel.setWidth(csize.width);
37303
37304         var hdHeight = this.mainHd.getHeight();
37305         var vw = csize.width;
37306         var vh = csize.height - (tbh + bbh);
37307
37308         s.setSize(vw, vh);
37309
37310         var bt = this.getBodyTable();
37311         var ltWidth = hasLock ?
37312                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
37313
37314         var scrollHeight = bt.offsetHeight;
37315         var scrollWidth = ltWidth + bt.offsetWidth;
37316         var vscroll = false, hscroll = false;
37317
37318         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
37319
37320         var lw = this.lockedWrap, mw = this.mainWrap;
37321         var lb = this.lockedBody, mb = this.mainBody;
37322
37323         setTimeout(function(){
37324             var t = s.dom.offsetTop;
37325             var w = s.dom.clientWidth,
37326                 h = s.dom.clientHeight;
37327
37328             lw.setTop(t);
37329             lw.setSize(ltWidth, h);
37330
37331             mw.setLeftTop(ltWidth, t);
37332             mw.setSize(w-ltWidth, h);
37333
37334             lb.setHeight(h-hdHeight);
37335             mb.setHeight(h-hdHeight);
37336
37337             if(is2ndPass !== true && !gv.userResized && expandCol){
37338                 // high speed resize without full column calculation
37339                 
37340                 var ci = cm.getIndexById(expandCol);
37341                 if (ci < 0) {
37342                     ci = cm.findColumnIndex(expandCol);
37343                 }
37344                 ci = Math.max(0, ci); // make sure it's got at least the first col.
37345                 var expandId = cm.getColumnId(ci);
37346                 var  tw = cm.getTotalWidth(false);
37347                 var currentWidth = cm.getColumnWidth(ci);
37348                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
37349                 if(currentWidth != cw){
37350                     cm.setColumnWidth(ci, cw, true);
37351                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
37352                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
37353                     gv.updateSplitters();
37354                     gv.layout(false, true);
37355                 }
37356             }
37357
37358             if(initialRender){
37359                 lw.show();
37360                 mw.show();
37361             }
37362             //c.endMeasure();
37363         }, 10);
37364     },
37365
37366     onWindowResize : function(){
37367         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
37368             return;
37369         }
37370         this.layout();
37371     },
37372
37373     appendFooter : function(parentEl){
37374         return null;
37375     },
37376
37377     sortAscText : "Sort Ascending",
37378     sortDescText : "Sort Descending",
37379     lockText : "Lock Column",
37380     unlockText : "Unlock Column",
37381     columnsText : "Columns"
37382 });
37383
37384
37385 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
37386     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
37387     this.proxy.el.addClass('x-grid3-col-dd');
37388 };
37389
37390 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
37391     handleMouseDown : function(e){
37392
37393     },
37394
37395     callHandleMouseDown : function(e){
37396         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
37397     }
37398 });
37399 /*
37400  * Based on:
37401  * Ext JS Library 1.1.1
37402  * Copyright(c) 2006-2007, Ext JS, LLC.
37403  *
37404  * Originally Released Under LGPL - original licence link has changed is not relivant.
37405  *
37406  * Fork - LGPL
37407  * <script type="text/javascript">
37408  */
37409  
37410 // private
37411 // This is a support class used internally by the Grid components
37412 Roo.grid.SplitDragZone = function(grid, hd, hd2){
37413     this.grid = grid;
37414     this.view = grid.getView();
37415     this.proxy = this.view.resizeProxy;
37416     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
37417         "gridSplitters" + this.grid.getGridEl().id, {
37418         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
37419     });
37420     this.setHandleElId(Roo.id(hd));
37421     this.setOuterHandleElId(Roo.id(hd2));
37422     this.scroll = false;
37423 };
37424 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
37425     fly: Roo.Element.fly,
37426
37427     b4StartDrag : function(x, y){
37428         this.view.headersDisabled = true;
37429         this.proxy.setHeight(this.view.mainWrap.getHeight());
37430         var w = this.cm.getColumnWidth(this.cellIndex);
37431         var minw = Math.max(w-this.grid.minColumnWidth, 0);
37432         this.resetConstraints();
37433         this.setXConstraint(minw, 1000);
37434         this.setYConstraint(0, 0);
37435         this.minX = x - minw;
37436         this.maxX = x + 1000;
37437         this.startPos = x;
37438         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
37439     },
37440
37441
37442     handleMouseDown : function(e){
37443         ev = Roo.EventObject.setEvent(e);
37444         var t = this.fly(ev.getTarget());
37445         if(t.hasClass("x-grid-split")){
37446             this.cellIndex = this.view.getCellIndex(t.dom);
37447             this.split = t.dom;
37448             this.cm = this.grid.colModel;
37449             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
37450                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
37451             }
37452         }
37453     },
37454
37455     endDrag : function(e){
37456         this.view.headersDisabled = false;
37457         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
37458         var diff = endX - this.startPos;
37459         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
37460     },
37461
37462     autoOffset : function(){
37463         this.setDelta(0,0);
37464     }
37465 });/*
37466  * Based on:
37467  * Ext JS Library 1.1.1
37468  * Copyright(c) 2006-2007, Ext JS, LLC.
37469  *
37470  * Originally Released Under LGPL - original licence link has changed is not relivant.
37471  *
37472  * Fork - LGPL
37473  * <script type="text/javascript">
37474  */
37475  
37476 // private
37477 // This is a support class used internally by the Grid components
37478 Roo.grid.GridDragZone = function(grid, config){
37479     this.view = grid.getView();
37480     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
37481     if(this.view.lockedBody){
37482         this.setHandleElId(Roo.id(this.view.mainBody.dom));
37483         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
37484     }
37485     this.scroll = false;
37486     this.grid = grid;
37487     this.ddel = document.createElement('div');
37488     this.ddel.className = 'x-grid-dd-wrap';
37489 };
37490
37491 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
37492     ddGroup : "GridDD",
37493
37494     getDragData : function(e){
37495         var t = Roo.lib.Event.getTarget(e);
37496         var rowIndex = this.view.findRowIndex(t);
37497         var sm = this.grid.selModel;
37498             
37499         //Roo.log(rowIndex);
37500         
37501         if (sm.getSelectedCell) {
37502             // cell selection..
37503             if (!sm.getSelectedCell()) {
37504                 return false;
37505             }
37506             if (rowIndex != sm.getSelectedCell()[0]) {
37507                 return false;
37508             }
37509         
37510         }
37511         
37512         if(rowIndex !== false){
37513             
37514             // if editorgrid.. 
37515             
37516             
37517             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
37518                
37519             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
37520               //  
37521             //}
37522             if (e.hasModifier()){
37523                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
37524             }
37525             
37526             Roo.log("getDragData");
37527             
37528             return {
37529                 grid: this.grid,
37530                 ddel: this.ddel,
37531                 rowIndex: rowIndex,
37532                 selections:sm.getSelections ? sm.getSelections() : (
37533                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
37534                 )
37535             };
37536         }
37537         return false;
37538     },
37539
37540     onInitDrag : function(e){
37541         var data = this.dragData;
37542         this.ddel.innerHTML = this.grid.getDragDropText();
37543         this.proxy.update(this.ddel);
37544         // fire start drag?
37545     },
37546
37547     afterRepair : function(){
37548         this.dragging = false;
37549     },
37550
37551     getRepairXY : function(e, data){
37552         return false;
37553     },
37554
37555     onEndDrag : function(data, e){
37556         // fire end drag?
37557     },
37558
37559     onValidDrop : function(dd, e, id){
37560         // fire drag drop?
37561         this.hideProxy();
37562     },
37563
37564     beforeInvalidDrop : function(e, id){
37565
37566     }
37567 });/*
37568  * Based on:
37569  * Ext JS Library 1.1.1
37570  * Copyright(c) 2006-2007, Ext JS, LLC.
37571  *
37572  * Originally Released Under LGPL - original licence link has changed is not relivant.
37573  *
37574  * Fork - LGPL
37575  * <script type="text/javascript">
37576  */
37577  
37578
37579 /**
37580  * @class Roo.grid.ColumnModel
37581  * @extends Roo.util.Observable
37582  * This is the default implementation of a ColumnModel used by the Grid. It defines
37583  * the columns in the grid.
37584  * <br>Usage:<br>
37585  <pre><code>
37586  var colModel = new Roo.grid.ColumnModel([
37587         {header: "Ticker", width: 60, sortable: true, locked: true},
37588         {header: "Company Name", width: 150, sortable: true},
37589         {header: "Market Cap.", width: 100, sortable: true},
37590         {header: "$ Sales", width: 100, sortable: true, renderer: money},
37591         {header: "Employees", width: 100, sortable: true, resizable: false}
37592  ]);
37593  </code></pre>
37594  * <p>
37595  
37596  * The config options listed for this class are options which may appear in each
37597  * individual column definition.
37598  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
37599  * @constructor
37600  * @param {Object} config An Array of column config objects. See this class's
37601  * config objects for details.
37602 */
37603 Roo.grid.ColumnModel = function(config){
37604         /**
37605      * The config passed into the constructor
37606      */
37607     this.config = config;
37608     this.lookup = {};
37609
37610     // if no id, create one
37611     // if the column does not have a dataIndex mapping,
37612     // map it to the order it is in the config
37613     for(var i = 0, len = config.length; i < len; i++){
37614         var c = config[i];
37615         if(typeof c.dataIndex == "undefined"){
37616             c.dataIndex = i;
37617         }
37618         if(typeof c.renderer == "string"){
37619             c.renderer = Roo.util.Format[c.renderer];
37620         }
37621         if(typeof c.id == "undefined"){
37622             c.id = Roo.id();
37623         }
37624         if(c.editor && c.editor.xtype){
37625             c.editor  = Roo.factory(c.editor, Roo.grid);
37626         }
37627         if(c.editor && c.editor.isFormField){
37628             c.editor = new Roo.grid.GridEditor(c.editor);
37629         }
37630         this.lookup[c.id] = c;
37631     }
37632
37633     /**
37634      * The width of columns which have no width specified (defaults to 100)
37635      * @type Number
37636      */
37637     this.defaultWidth = 100;
37638
37639     /**
37640      * Default sortable of columns which have no sortable specified (defaults to false)
37641      * @type Boolean
37642      */
37643     this.defaultSortable = false;
37644
37645     this.addEvents({
37646         /**
37647              * @event widthchange
37648              * Fires when the width of a column changes.
37649              * @param {ColumnModel} this
37650              * @param {Number} columnIndex The column index
37651              * @param {Number} newWidth The new width
37652              */
37653             "widthchange": true,
37654         /**
37655              * @event headerchange
37656              * Fires when the text of a header changes.
37657              * @param {ColumnModel} this
37658              * @param {Number} columnIndex The column index
37659              * @param {Number} newText The new header text
37660              */
37661             "headerchange": true,
37662         /**
37663              * @event hiddenchange
37664              * Fires when a column is hidden or "unhidden".
37665              * @param {ColumnModel} this
37666              * @param {Number} columnIndex The column index
37667              * @param {Boolean} hidden true if hidden, false otherwise
37668              */
37669             "hiddenchange": true,
37670             /**
37671          * @event columnmoved
37672          * Fires when a column is moved.
37673          * @param {ColumnModel} this
37674          * @param {Number} oldIndex
37675          * @param {Number} newIndex
37676          */
37677         "columnmoved" : true,
37678         /**
37679          * @event columlockchange
37680          * Fires when a column's locked state is changed
37681          * @param {ColumnModel} this
37682          * @param {Number} colIndex
37683          * @param {Boolean} locked true if locked
37684          */
37685         "columnlockchange" : true
37686     });
37687     Roo.grid.ColumnModel.superclass.constructor.call(this);
37688 };
37689 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
37690     /**
37691      * @cfg {String} header The header text to display in the Grid view.
37692      */
37693     /**
37694      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
37695      * {@link Roo.data.Record} definition from which to draw the column's value. If not
37696      * specified, the column's index is used as an index into the Record's data Array.
37697      */
37698     /**
37699      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
37700      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
37701      */
37702     /**
37703      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
37704      * Defaults to the value of the {@link #defaultSortable} property.
37705      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
37706      */
37707     /**
37708      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
37709      */
37710     /**
37711      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
37712      */
37713     /**
37714      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
37715      */
37716     /**
37717      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
37718      */
37719     /**
37720      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
37721      * given the cell's data value. See {@link #setRenderer}. If not specified, the
37722      * default renderer uses the raw data value.
37723      */
37724        /**
37725      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
37726      */
37727     /**
37728      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
37729      */
37730
37731     /**
37732      * Returns the id of the column at the specified index.
37733      * @param {Number} index The column index
37734      * @return {String} the id
37735      */
37736     getColumnId : function(index){
37737         return this.config[index].id;
37738     },
37739
37740     /**
37741      * Returns the column for a specified id.
37742      * @param {String} id The column id
37743      * @return {Object} the column
37744      */
37745     getColumnById : function(id){
37746         return this.lookup[id];
37747     },
37748
37749     
37750     /**
37751      * Returns the column for a specified dataIndex.
37752      * @param {String} dataIndex The column dataIndex
37753      * @return {Object|Boolean} the column or false if not found
37754      */
37755     getColumnByDataIndex: function(dataIndex){
37756         var index = this.findColumnIndex(dataIndex);
37757         return index > -1 ? this.config[index] : false;
37758     },
37759     
37760     /**
37761      * Returns the index for a specified column id.
37762      * @param {String} id The column id
37763      * @return {Number} the index, or -1 if not found
37764      */
37765     getIndexById : function(id){
37766         for(var i = 0, len = this.config.length; i < len; i++){
37767             if(this.config[i].id == id){
37768                 return i;
37769             }
37770         }
37771         return -1;
37772     },
37773     
37774     /**
37775      * Returns the index for a specified column dataIndex.
37776      * @param {String} dataIndex The column dataIndex
37777      * @return {Number} the index, or -1 if not found
37778      */
37779     
37780     findColumnIndex : function(dataIndex){
37781         for(var i = 0, len = this.config.length; i < len; i++){
37782             if(this.config[i].dataIndex == dataIndex){
37783                 return i;
37784             }
37785         }
37786         return -1;
37787     },
37788     
37789     
37790     moveColumn : function(oldIndex, newIndex){
37791         var c = this.config[oldIndex];
37792         this.config.splice(oldIndex, 1);
37793         this.config.splice(newIndex, 0, c);
37794         this.dataMap = null;
37795         this.fireEvent("columnmoved", this, oldIndex, newIndex);
37796     },
37797
37798     isLocked : function(colIndex){
37799         return this.config[colIndex].locked === true;
37800     },
37801
37802     setLocked : function(colIndex, value, suppressEvent){
37803         if(this.isLocked(colIndex) == value){
37804             return;
37805         }
37806         this.config[colIndex].locked = value;
37807         if(!suppressEvent){
37808             this.fireEvent("columnlockchange", this, colIndex, value);
37809         }
37810     },
37811
37812     getTotalLockedWidth : function(){
37813         var totalWidth = 0;
37814         for(var i = 0; i < this.config.length; i++){
37815             if(this.isLocked(i) && !this.isHidden(i)){
37816                 this.totalWidth += this.getColumnWidth(i);
37817             }
37818         }
37819         return totalWidth;
37820     },
37821
37822     getLockedCount : function(){
37823         for(var i = 0, len = this.config.length; i < len; i++){
37824             if(!this.isLocked(i)){
37825                 return i;
37826             }
37827         }
37828     },
37829
37830     /**
37831      * Returns the number of columns.
37832      * @return {Number}
37833      */
37834     getColumnCount : function(visibleOnly){
37835         if(visibleOnly === true){
37836             var c = 0;
37837             for(var i = 0, len = this.config.length; i < len; i++){
37838                 if(!this.isHidden(i)){
37839                     c++;
37840                 }
37841             }
37842             return c;
37843         }
37844         return this.config.length;
37845     },
37846
37847     /**
37848      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
37849      * @param {Function} fn
37850      * @param {Object} scope (optional)
37851      * @return {Array} result
37852      */
37853     getColumnsBy : function(fn, scope){
37854         var r = [];
37855         for(var i = 0, len = this.config.length; i < len; i++){
37856             var c = this.config[i];
37857             if(fn.call(scope||this, c, i) === true){
37858                 r[r.length] = c;
37859             }
37860         }
37861         return r;
37862     },
37863
37864     /**
37865      * Returns true if the specified column is sortable.
37866      * @param {Number} col The column index
37867      * @return {Boolean}
37868      */
37869     isSortable : function(col){
37870         if(typeof this.config[col].sortable == "undefined"){
37871             return this.defaultSortable;
37872         }
37873         return this.config[col].sortable;
37874     },
37875
37876     /**
37877      * Returns the rendering (formatting) function defined for the column.
37878      * @param {Number} col The column index.
37879      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
37880      */
37881     getRenderer : function(col){
37882         if(!this.config[col].renderer){
37883             return Roo.grid.ColumnModel.defaultRenderer;
37884         }
37885         return this.config[col].renderer;
37886     },
37887
37888     /**
37889      * Sets the rendering (formatting) function for a column.
37890      * @param {Number} col The column index
37891      * @param {Function} fn The function to use to process the cell's raw data
37892      * to return HTML markup for the grid view. The render function is called with
37893      * the following parameters:<ul>
37894      * <li>Data value.</li>
37895      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
37896      * <li>css A CSS style string to apply to the table cell.</li>
37897      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
37898      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
37899      * <li>Row index</li>
37900      * <li>Column index</li>
37901      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
37902      */
37903     setRenderer : function(col, fn){
37904         this.config[col].renderer = fn;
37905     },
37906
37907     /**
37908      * Returns the width for the specified column.
37909      * @param {Number} col The column index
37910      * @return {Number}
37911      */
37912     getColumnWidth : function(col){
37913         return this.config[col].width * 1 || this.defaultWidth;
37914     },
37915
37916     /**
37917      * Sets the width for a column.
37918      * @param {Number} col The column index
37919      * @param {Number} width The new width
37920      */
37921     setColumnWidth : function(col, width, suppressEvent){
37922         this.config[col].width = width;
37923         this.totalWidth = null;
37924         if(!suppressEvent){
37925              this.fireEvent("widthchange", this, col, width);
37926         }
37927     },
37928
37929     /**
37930      * Returns the total width of all columns.
37931      * @param {Boolean} includeHidden True to include hidden column widths
37932      * @return {Number}
37933      */
37934     getTotalWidth : function(includeHidden){
37935         if(!this.totalWidth){
37936             this.totalWidth = 0;
37937             for(var i = 0, len = this.config.length; i < len; i++){
37938                 if(includeHidden || !this.isHidden(i)){
37939                     this.totalWidth += this.getColumnWidth(i);
37940                 }
37941             }
37942         }
37943         return this.totalWidth;
37944     },
37945
37946     /**
37947      * Returns the header for the specified column.
37948      * @param {Number} col The column index
37949      * @return {String}
37950      */
37951     getColumnHeader : function(col){
37952         return this.config[col].header;
37953     },
37954
37955     /**
37956      * Sets the header for a column.
37957      * @param {Number} col The column index
37958      * @param {String} header The new header
37959      */
37960     setColumnHeader : function(col, header){
37961         this.config[col].header = header;
37962         this.fireEvent("headerchange", this, col, header);
37963     },
37964
37965     /**
37966      * Returns the tooltip for the specified column.
37967      * @param {Number} col The column index
37968      * @return {String}
37969      */
37970     getColumnTooltip : function(col){
37971             return this.config[col].tooltip;
37972     },
37973     /**
37974      * Sets the tooltip for a column.
37975      * @param {Number} col The column index
37976      * @param {String} tooltip The new tooltip
37977      */
37978     setColumnTooltip : function(col, tooltip){
37979             this.config[col].tooltip = tooltip;
37980     },
37981
37982     /**
37983      * Returns the dataIndex for the specified column.
37984      * @param {Number} col The column index
37985      * @return {Number}
37986      */
37987     getDataIndex : function(col){
37988         return this.config[col].dataIndex;
37989     },
37990
37991     /**
37992      * Sets the dataIndex for a column.
37993      * @param {Number} col The column index
37994      * @param {Number} dataIndex The new dataIndex
37995      */
37996     setDataIndex : function(col, dataIndex){
37997         this.config[col].dataIndex = dataIndex;
37998     },
37999
38000     
38001     
38002     /**
38003      * Returns true if the cell is editable.
38004      * @param {Number} colIndex The column index
38005      * @param {Number} rowIndex The row index
38006      * @return {Boolean}
38007      */
38008     isCellEditable : function(colIndex, rowIndex){
38009         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
38010     },
38011
38012     /**
38013      * Returns the editor defined for the cell/column.
38014      * return false or null to disable editing.
38015      * @param {Number} colIndex The column index
38016      * @param {Number} rowIndex The row index
38017      * @return {Object}
38018      */
38019     getCellEditor : function(colIndex, rowIndex){
38020         return this.config[colIndex].editor;
38021     },
38022
38023     /**
38024      * Sets if a column is editable.
38025      * @param {Number} col The column index
38026      * @param {Boolean} editable True if the column is editable
38027      */
38028     setEditable : function(col, editable){
38029         this.config[col].editable = editable;
38030     },
38031
38032
38033     /**
38034      * Returns true if the column is hidden.
38035      * @param {Number} colIndex The column index
38036      * @return {Boolean}
38037      */
38038     isHidden : function(colIndex){
38039         return this.config[colIndex].hidden;
38040     },
38041
38042
38043     /**
38044      * Returns true if the column width cannot be changed
38045      */
38046     isFixed : function(colIndex){
38047         return this.config[colIndex].fixed;
38048     },
38049
38050     /**
38051      * Returns true if the column can be resized
38052      * @return {Boolean}
38053      */
38054     isResizable : function(colIndex){
38055         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
38056     },
38057     /**
38058      * Sets if a column is hidden.
38059      * @param {Number} colIndex The column index
38060      * @param {Boolean} hidden True if the column is hidden
38061      */
38062     setHidden : function(colIndex, hidden){
38063         this.config[colIndex].hidden = hidden;
38064         this.totalWidth = null;
38065         this.fireEvent("hiddenchange", this, colIndex, hidden);
38066     },
38067
38068     /**
38069      * Sets the editor for a column.
38070      * @param {Number} col The column index
38071      * @param {Object} editor The editor object
38072      */
38073     setEditor : function(col, editor){
38074         this.config[col].editor = editor;
38075     }
38076 });
38077
38078 Roo.grid.ColumnModel.defaultRenderer = function(value){
38079         if(typeof value == "string" && value.length < 1){
38080             return "&#160;";
38081         }
38082         return value;
38083 };
38084
38085 // Alias for backwards compatibility
38086 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
38087 /*
38088  * Based on:
38089  * Ext JS Library 1.1.1
38090  * Copyright(c) 2006-2007, Ext JS, LLC.
38091  *
38092  * Originally Released Under LGPL - original licence link has changed is not relivant.
38093  *
38094  * Fork - LGPL
38095  * <script type="text/javascript">
38096  */
38097
38098 /**
38099  * @class Roo.grid.AbstractSelectionModel
38100  * @extends Roo.util.Observable
38101  * Abstract base class for grid SelectionModels.  It provides the interface that should be
38102  * implemented by descendant classes.  This class should not be directly instantiated.
38103  * @constructor
38104  */
38105 Roo.grid.AbstractSelectionModel = function(){
38106     this.locked = false;
38107     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
38108 };
38109
38110 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
38111     /** @ignore Called by the grid automatically. Do not call directly. */
38112     init : function(grid){
38113         this.grid = grid;
38114         this.initEvents();
38115     },
38116
38117     /**
38118      * Locks the selections.
38119      */
38120     lock : function(){
38121         this.locked = true;
38122     },
38123
38124     /**
38125      * Unlocks the selections.
38126      */
38127     unlock : function(){
38128         this.locked = false;
38129     },
38130
38131     /**
38132      * Returns true if the selections are locked.
38133      * @return {Boolean}
38134      */
38135     isLocked : function(){
38136         return this.locked;
38137     }
38138 });/*
38139  * Based on:
38140  * Ext JS Library 1.1.1
38141  * Copyright(c) 2006-2007, Ext JS, LLC.
38142  *
38143  * Originally Released Under LGPL - original licence link has changed is not relivant.
38144  *
38145  * Fork - LGPL
38146  * <script type="text/javascript">
38147  */
38148 /**
38149  * @extends Roo.grid.AbstractSelectionModel
38150  * @class Roo.grid.RowSelectionModel
38151  * The default SelectionModel used by {@link Roo.grid.Grid}.
38152  * It supports multiple selections and keyboard selection/navigation. 
38153  * @constructor
38154  * @param {Object} config
38155  */
38156 Roo.grid.RowSelectionModel = function(config){
38157     Roo.apply(this, config);
38158     this.selections = new Roo.util.MixedCollection(false, function(o){
38159         return o.id;
38160     });
38161
38162     this.last = false;
38163     this.lastActive = false;
38164
38165     this.addEvents({
38166         /**
38167              * @event selectionchange
38168              * Fires when the selection changes
38169              * @param {SelectionModel} this
38170              */
38171             "selectionchange" : true,
38172         /**
38173              * @event afterselectionchange
38174              * Fires after the selection changes (eg. by key press or clicking)
38175              * @param {SelectionModel} this
38176              */
38177             "afterselectionchange" : true,
38178         /**
38179              * @event beforerowselect
38180              * Fires when a row is selected being selected, return false to cancel.
38181              * @param {SelectionModel} this
38182              * @param {Number} rowIndex The selected index
38183              * @param {Boolean} keepExisting False if other selections will be cleared
38184              */
38185             "beforerowselect" : true,
38186         /**
38187              * @event rowselect
38188              * Fires when a row is selected.
38189              * @param {SelectionModel} this
38190              * @param {Number} rowIndex The selected index
38191              * @param {Roo.data.Record} r The record
38192              */
38193             "rowselect" : true,
38194         /**
38195              * @event rowdeselect
38196              * Fires when a row is deselected.
38197              * @param {SelectionModel} this
38198              * @param {Number} rowIndex The selected index
38199              */
38200         "rowdeselect" : true
38201     });
38202     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
38203     this.locked = false;
38204 };
38205
38206 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
38207     /**
38208      * @cfg {Boolean} singleSelect
38209      * True to allow selection of only one row at a time (defaults to false)
38210      */
38211     singleSelect : false,
38212
38213     // private
38214     initEvents : function(){
38215
38216         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
38217             this.grid.on("mousedown", this.handleMouseDown, this);
38218         }else{ // allow click to work like normal
38219             this.grid.on("rowclick", this.handleDragableRowClick, this);
38220         }
38221
38222         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
38223             "up" : function(e){
38224                 if(!e.shiftKey){
38225                     this.selectPrevious(e.shiftKey);
38226                 }else if(this.last !== false && this.lastActive !== false){
38227                     var last = this.last;
38228                     this.selectRange(this.last,  this.lastActive-1);
38229                     this.grid.getView().focusRow(this.lastActive);
38230                     if(last !== false){
38231                         this.last = last;
38232                     }
38233                 }else{
38234                     this.selectFirstRow();
38235                 }
38236                 this.fireEvent("afterselectionchange", this);
38237             },
38238             "down" : function(e){
38239                 if(!e.shiftKey){
38240                     this.selectNext(e.shiftKey);
38241                 }else if(this.last !== false && this.lastActive !== false){
38242                     var last = this.last;
38243                     this.selectRange(this.last,  this.lastActive+1);
38244                     this.grid.getView().focusRow(this.lastActive);
38245                     if(last !== false){
38246                         this.last = last;
38247                     }
38248                 }else{
38249                     this.selectFirstRow();
38250                 }
38251                 this.fireEvent("afterselectionchange", this);
38252             },
38253             scope: this
38254         });
38255
38256         var view = this.grid.view;
38257         view.on("refresh", this.onRefresh, this);
38258         view.on("rowupdated", this.onRowUpdated, this);
38259         view.on("rowremoved", this.onRemove, this);
38260     },
38261
38262     // private
38263     onRefresh : function(){
38264         var ds = this.grid.dataSource, i, v = this.grid.view;
38265         var s = this.selections;
38266         s.each(function(r){
38267             if((i = ds.indexOfId(r.id)) != -1){
38268                 v.onRowSelect(i);
38269             }else{
38270                 s.remove(r);
38271             }
38272         });
38273     },
38274
38275     // private
38276     onRemove : function(v, index, r){
38277         this.selections.remove(r);
38278     },
38279
38280     // private
38281     onRowUpdated : function(v, index, r){
38282         if(this.isSelected(r)){
38283             v.onRowSelect(index);
38284         }
38285     },
38286
38287     /**
38288      * Select records.
38289      * @param {Array} records The records to select
38290      * @param {Boolean} keepExisting (optional) True to keep existing selections
38291      */
38292     selectRecords : function(records, keepExisting){
38293         if(!keepExisting){
38294             this.clearSelections();
38295         }
38296         var ds = this.grid.dataSource;
38297         for(var i = 0, len = records.length; i < len; i++){
38298             this.selectRow(ds.indexOf(records[i]), true);
38299         }
38300     },
38301
38302     /**
38303      * Gets the number of selected rows.
38304      * @return {Number}
38305      */
38306     getCount : function(){
38307         return this.selections.length;
38308     },
38309
38310     /**
38311      * Selects the first row in the grid.
38312      */
38313     selectFirstRow : function(){
38314         this.selectRow(0);
38315     },
38316
38317     /**
38318      * Select the last row.
38319      * @param {Boolean} keepExisting (optional) True to keep existing selections
38320      */
38321     selectLastRow : function(keepExisting){
38322         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
38323     },
38324
38325     /**
38326      * Selects the row immediately following the last selected row.
38327      * @param {Boolean} keepExisting (optional) True to keep existing selections
38328      */
38329     selectNext : function(keepExisting){
38330         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
38331             this.selectRow(this.last+1, keepExisting);
38332             this.grid.getView().focusRow(this.last);
38333         }
38334     },
38335
38336     /**
38337      * Selects the row that precedes the last selected row.
38338      * @param {Boolean} keepExisting (optional) True to keep existing selections
38339      */
38340     selectPrevious : function(keepExisting){
38341         if(this.last){
38342             this.selectRow(this.last-1, keepExisting);
38343             this.grid.getView().focusRow(this.last);
38344         }
38345     },
38346
38347     /**
38348      * Returns the selected records
38349      * @return {Array} Array of selected records
38350      */
38351     getSelections : function(){
38352         return [].concat(this.selections.items);
38353     },
38354
38355     /**
38356      * Returns the first selected record.
38357      * @return {Record}
38358      */
38359     getSelected : function(){
38360         return this.selections.itemAt(0);
38361     },
38362
38363
38364     /**
38365      * Clears all selections.
38366      */
38367     clearSelections : function(fast){
38368         if(this.locked) return;
38369         if(fast !== true){
38370             var ds = this.grid.dataSource;
38371             var s = this.selections;
38372             s.each(function(r){
38373                 this.deselectRow(ds.indexOfId(r.id));
38374             }, this);
38375             s.clear();
38376         }else{
38377             this.selections.clear();
38378         }
38379         this.last = false;
38380     },
38381
38382
38383     /**
38384      * Selects all rows.
38385      */
38386     selectAll : function(){
38387         if(this.locked) return;
38388         this.selections.clear();
38389         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
38390             this.selectRow(i, true);
38391         }
38392     },
38393
38394     /**
38395      * Returns True if there is a selection.
38396      * @return {Boolean}
38397      */
38398     hasSelection : function(){
38399         return this.selections.length > 0;
38400     },
38401
38402     /**
38403      * Returns True if the specified row is selected.
38404      * @param {Number/Record} record The record or index of the record to check
38405      * @return {Boolean}
38406      */
38407     isSelected : function(index){
38408         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
38409         return (r && this.selections.key(r.id) ? true : false);
38410     },
38411
38412     /**
38413      * Returns True if the specified record id is selected.
38414      * @param {String} id The id of record to check
38415      * @return {Boolean}
38416      */
38417     isIdSelected : function(id){
38418         return (this.selections.key(id) ? true : false);
38419     },
38420
38421     // private
38422     handleMouseDown : function(e, t){
38423         var view = this.grid.getView(), rowIndex;
38424         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
38425             return;
38426         };
38427         if(e.shiftKey && this.last !== false){
38428             var last = this.last;
38429             this.selectRange(last, rowIndex, e.ctrlKey);
38430             this.last = last; // reset the last
38431             view.focusRow(rowIndex);
38432         }else{
38433             var isSelected = this.isSelected(rowIndex);
38434             if(e.button !== 0 && isSelected){
38435                 view.focusRow(rowIndex);
38436             }else if(e.ctrlKey && isSelected){
38437                 this.deselectRow(rowIndex);
38438             }else if(!isSelected){
38439                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
38440                 view.focusRow(rowIndex);
38441             }
38442         }
38443         this.fireEvent("afterselectionchange", this);
38444     },
38445     // private
38446     handleDragableRowClick :  function(grid, rowIndex, e) 
38447     {
38448         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
38449             this.selectRow(rowIndex, false);
38450             grid.view.focusRow(rowIndex);
38451              this.fireEvent("afterselectionchange", this);
38452         }
38453     },
38454     
38455     /**
38456      * Selects multiple rows.
38457      * @param {Array} rows Array of the indexes of the row to select
38458      * @param {Boolean} keepExisting (optional) True to keep existing selections
38459      */
38460     selectRows : function(rows, keepExisting){
38461         if(!keepExisting){
38462             this.clearSelections();
38463         }
38464         for(var i = 0, len = rows.length; i < len; i++){
38465             this.selectRow(rows[i], true);
38466         }
38467     },
38468
38469     /**
38470      * Selects a range of rows. All rows in between startRow and endRow are also selected.
38471      * @param {Number} startRow The index of the first row in the range
38472      * @param {Number} endRow The index of the last row in the range
38473      * @param {Boolean} keepExisting (optional) True to retain existing selections
38474      */
38475     selectRange : function(startRow, endRow, keepExisting){
38476         if(this.locked) return;
38477         if(!keepExisting){
38478             this.clearSelections();
38479         }
38480         if(startRow <= endRow){
38481             for(var i = startRow; i <= endRow; i++){
38482                 this.selectRow(i, true);
38483             }
38484         }else{
38485             for(var i = startRow; i >= endRow; i--){
38486                 this.selectRow(i, true);
38487             }
38488         }
38489     },
38490
38491     /**
38492      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
38493      * @param {Number} startRow The index of the first row in the range
38494      * @param {Number} endRow The index of the last row in the range
38495      */
38496     deselectRange : function(startRow, endRow, preventViewNotify){
38497         if(this.locked) return;
38498         for(var i = startRow; i <= endRow; i++){
38499             this.deselectRow(i, preventViewNotify);
38500         }
38501     },
38502
38503     /**
38504      * Selects a row.
38505      * @param {Number} row The index of the row to select
38506      * @param {Boolean} keepExisting (optional) True to keep existing selections
38507      */
38508     selectRow : function(index, keepExisting, preventViewNotify){
38509         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
38510         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
38511             if(!keepExisting || this.singleSelect){
38512                 this.clearSelections();
38513             }
38514             var r = this.grid.dataSource.getAt(index);
38515             this.selections.add(r);
38516             this.last = this.lastActive = index;
38517             if(!preventViewNotify){
38518                 this.grid.getView().onRowSelect(index);
38519             }
38520             this.fireEvent("rowselect", this, index, r);
38521             this.fireEvent("selectionchange", this);
38522         }
38523     },
38524
38525     /**
38526      * Deselects a row.
38527      * @param {Number} row The index of the row to deselect
38528      */
38529     deselectRow : function(index, preventViewNotify){
38530         if(this.locked) return;
38531         if(this.last == index){
38532             this.last = false;
38533         }
38534         if(this.lastActive == index){
38535             this.lastActive = false;
38536         }
38537         var r = this.grid.dataSource.getAt(index);
38538         this.selections.remove(r);
38539         if(!preventViewNotify){
38540             this.grid.getView().onRowDeselect(index);
38541         }
38542         this.fireEvent("rowdeselect", this, index);
38543         this.fireEvent("selectionchange", this);
38544     },
38545
38546     // private
38547     restoreLast : function(){
38548         if(this._last){
38549             this.last = this._last;
38550         }
38551     },
38552
38553     // private
38554     acceptsNav : function(row, col, cm){
38555         return !cm.isHidden(col) && cm.isCellEditable(col, row);
38556     },
38557
38558     // private
38559     onEditorKey : function(field, e){
38560         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
38561         if(k == e.TAB){
38562             e.stopEvent();
38563             ed.completeEdit();
38564             if(e.shiftKey){
38565                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
38566             }else{
38567                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
38568             }
38569         }else if(k == e.ENTER && !e.ctrlKey){
38570             e.stopEvent();
38571             ed.completeEdit();
38572             if(e.shiftKey){
38573                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
38574             }else{
38575                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
38576             }
38577         }else if(k == e.ESC){
38578             ed.cancelEdit();
38579         }
38580         if(newCell){
38581             g.startEditing(newCell[0], newCell[1]);
38582         }
38583     }
38584 });/*
38585  * Based on:
38586  * Ext JS Library 1.1.1
38587  * Copyright(c) 2006-2007, Ext JS, LLC.
38588  *
38589  * Originally Released Under LGPL - original licence link has changed is not relivant.
38590  *
38591  * Fork - LGPL
38592  * <script type="text/javascript">
38593  */
38594 /**
38595  * @class Roo.grid.CellSelectionModel
38596  * @extends Roo.grid.AbstractSelectionModel
38597  * This class provides the basic implementation for cell selection in a grid.
38598  * @constructor
38599  * @param {Object} config The object containing the configuration of this model.
38600  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
38601  */
38602 Roo.grid.CellSelectionModel = function(config){
38603     Roo.apply(this, config);
38604
38605     this.selection = null;
38606
38607     this.addEvents({
38608         /**
38609              * @event beforerowselect
38610              * Fires before a cell is selected.
38611              * @param {SelectionModel} this
38612              * @param {Number} rowIndex The selected row index
38613              * @param {Number} colIndex The selected cell index
38614              */
38615             "beforecellselect" : true,
38616         /**
38617              * @event cellselect
38618              * Fires when a cell is selected.
38619              * @param {SelectionModel} this
38620              * @param {Number} rowIndex The selected row index
38621              * @param {Number} colIndex The selected cell index
38622              */
38623             "cellselect" : true,
38624         /**
38625              * @event selectionchange
38626              * Fires when the active selection changes.
38627              * @param {SelectionModel} this
38628              * @param {Object} selection null for no selection or an object (o) with two properties
38629                 <ul>
38630                 <li>o.record: the record object for the row the selection is in</li>
38631                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
38632                 </ul>
38633              */
38634             "selectionchange" : true,
38635         /**
38636              * @event tabend
38637              * Fires when the tab (or enter) was pressed on the last editable cell
38638              * You can use this to trigger add new row.
38639              * @param {SelectionModel} this
38640              */
38641             "tabend" : true,
38642          /**
38643              * @event beforeeditnext
38644              * Fires before the next editable sell is made active
38645              * You can use this to skip to another cell or fire the tabend
38646              *    if you set cell to false
38647              * @param {Object} eventdata object : { cell : [ row, col ] } 
38648              */
38649             "beforeeditnext" : true
38650     });
38651     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
38652 };
38653
38654 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
38655     
38656     enter_is_tab: false,
38657
38658     /** @ignore */
38659     initEvents : function(){
38660         this.grid.on("mousedown", this.handleMouseDown, this);
38661         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
38662         var view = this.grid.view;
38663         view.on("refresh", this.onViewChange, this);
38664         view.on("rowupdated", this.onRowUpdated, this);
38665         view.on("beforerowremoved", this.clearSelections, this);
38666         view.on("beforerowsinserted", this.clearSelections, this);
38667         if(this.grid.isEditor){
38668             this.grid.on("beforeedit", this.beforeEdit,  this);
38669         }
38670     },
38671
38672         //private
38673     beforeEdit : function(e){
38674         this.select(e.row, e.column, false, true, e.record);
38675     },
38676
38677         //private
38678     onRowUpdated : function(v, index, r){
38679         if(this.selection && this.selection.record == r){
38680             v.onCellSelect(index, this.selection.cell[1]);
38681         }
38682     },
38683
38684         //private
38685     onViewChange : function(){
38686         this.clearSelections(true);
38687     },
38688
38689         /**
38690          * Returns the currently selected cell,.
38691          * @return {Array} The selected cell (row, column) or null if none selected.
38692          */
38693     getSelectedCell : function(){
38694         return this.selection ? this.selection.cell : null;
38695     },
38696
38697     /**
38698      * Clears all selections.
38699      * @param {Boolean} true to prevent the gridview from being notified about the change.
38700      */
38701     clearSelections : function(preventNotify){
38702         var s = this.selection;
38703         if(s){
38704             if(preventNotify !== true){
38705                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
38706             }
38707             this.selection = null;
38708             this.fireEvent("selectionchange", this, null);
38709         }
38710     },
38711
38712     /**
38713      * Returns true if there is a selection.
38714      * @return {Boolean}
38715      */
38716     hasSelection : function(){
38717         return this.selection ? true : false;
38718     },
38719
38720     /** @ignore */
38721     handleMouseDown : function(e, t){
38722         var v = this.grid.getView();
38723         if(this.isLocked()){
38724             return;
38725         };
38726         var row = v.findRowIndex(t);
38727         var cell = v.findCellIndex(t);
38728         if(row !== false && cell !== false){
38729             this.select(row, cell);
38730         }
38731     },
38732
38733     /**
38734      * Selects a cell.
38735      * @param {Number} rowIndex
38736      * @param {Number} collIndex
38737      */
38738     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
38739         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
38740             this.clearSelections();
38741             r = r || this.grid.dataSource.getAt(rowIndex);
38742             this.selection = {
38743                 record : r,
38744                 cell : [rowIndex, colIndex]
38745             };
38746             if(!preventViewNotify){
38747                 var v = this.grid.getView();
38748                 v.onCellSelect(rowIndex, colIndex);
38749                 if(preventFocus !== true){
38750                     v.focusCell(rowIndex, colIndex);
38751                 }
38752             }
38753             this.fireEvent("cellselect", this, rowIndex, colIndex);
38754             this.fireEvent("selectionchange", this, this.selection);
38755         }
38756     },
38757
38758         //private
38759     isSelectable : function(rowIndex, colIndex, cm){
38760         return !cm.isHidden(colIndex);
38761     },
38762
38763     /** @ignore */
38764     handleKeyDown : function(e){
38765         //Roo.log('Cell Sel Model handleKeyDown');
38766         if(!e.isNavKeyPress()){
38767             return;
38768         }
38769         var g = this.grid, s = this.selection;
38770         if(!s){
38771             e.stopEvent();
38772             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
38773             if(cell){
38774                 this.select(cell[0], cell[1]);
38775             }
38776             return;
38777         }
38778         var sm = this;
38779         var walk = function(row, col, step){
38780             return g.walkCells(row, col, step, sm.isSelectable,  sm);
38781         };
38782         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
38783         var newCell;
38784
38785       
38786
38787         switch(k){
38788             case e.TAB:
38789                 // handled by onEditorKey
38790                 if (g.isEditor && g.editing) {
38791                     return;
38792                 }
38793                 if(e.shiftKey) {
38794                     newCell = walk(r, c-1, -1);
38795                 } else {
38796                     newCell = walk(r, c+1, 1);
38797                 }
38798                 break;
38799             
38800             case e.DOWN:
38801                newCell = walk(r+1, c, 1);
38802                 break;
38803             
38804             case e.UP:
38805                 newCell = walk(r-1, c, -1);
38806                 break;
38807             
38808             case e.RIGHT:
38809                 newCell = walk(r, c+1, 1);
38810                 break;
38811             
38812             case e.LEFT:
38813                 newCell = walk(r, c-1, -1);
38814                 break;
38815             
38816             case e.ENTER:
38817                 
38818                 if(g.isEditor && !g.editing){
38819                    g.startEditing(r, c);
38820                    e.stopEvent();
38821                    return;
38822                 }
38823                 
38824                 
38825              break;
38826         };
38827         if(newCell){
38828             this.select(newCell[0], newCell[1]);
38829             e.stopEvent();
38830             
38831         }
38832     },
38833
38834     acceptsNav : function(row, col, cm){
38835         return !cm.isHidden(col) && cm.isCellEditable(col, row);
38836     },
38837     /**
38838      * Selects a cell.
38839      * @param {Number} field (not used) - as it's normally used as a listener
38840      * @param {Number} e - event - fake it by using
38841      *
38842      * var e = Roo.EventObjectImpl.prototype;
38843      * e.keyCode = e.TAB
38844      *
38845      * 
38846      */
38847     onEditorKey : function(field, e){
38848         
38849         var k = e.getKey(),
38850             newCell,
38851             g = this.grid,
38852             ed = g.activeEditor,
38853             forward = false;
38854         ///Roo.log('onEditorKey' + k);
38855         
38856         
38857         if (this.enter_is_tab && k == e.ENTER) {
38858             k = e.TAB;
38859         }
38860         
38861         if(k == e.TAB){
38862             if(e.shiftKey){
38863                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
38864             }else{
38865                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
38866                 forward = true;
38867             }
38868             
38869             e.stopEvent();
38870             
38871         } else if(k == e.ENTER &&  !e.ctrlKey){
38872             ed.completeEdit();
38873             e.stopEvent();
38874             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
38875         
38876                 } else if(k == e.ESC){
38877             ed.cancelEdit();
38878         }
38879                 
38880         if (newCell) {
38881             var ecall = { cell : newCell, forward : forward };
38882             this.fireEvent('beforeeditnext', ecall );
38883             newCell = ecall.cell;
38884                         forward = ecall.forward;
38885         }
38886                 
38887         if(newCell){
38888             //Roo.log('next cell after edit');
38889             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
38890         } else if (forward) {
38891             // tabbed past last
38892             this.fireEvent.defer(100, this, ['tabend',this]);
38893         }
38894     }
38895 });/*
38896  * Based on:
38897  * Ext JS Library 1.1.1
38898  * Copyright(c) 2006-2007, Ext JS, LLC.
38899  *
38900  * Originally Released Under LGPL - original licence link has changed is not relivant.
38901  *
38902  * Fork - LGPL
38903  * <script type="text/javascript">
38904  */
38905  
38906 /**
38907  * @class Roo.grid.EditorGrid
38908  * @extends Roo.grid.Grid
38909  * Class for creating and editable grid.
38910  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
38911  * The container MUST have some type of size defined for the grid to fill. The container will be 
38912  * automatically set to position relative if it isn't already.
38913  * @param {Object} dataSource The data model to bind to
38914  * @param {Object} colModel The column model with info about this grid's columns
38915  */
38916 Roo.grid.EditorGrid = function(container, config){
38917     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
38918     this.getGridEl().addClass("xedit-grid");
38919
38920     if(!this.selModel){
38921         this.selModel = new Roo.grid.CellSelectionModel();
38922     }
38923
38924     this.activeEditor = null;
38925
38926         this.addEvents({
38927             /**
38928              * @event beforeedit
38929              * Fires before cell editing is triggered. The edit event object has the following properties <br />
38930              * <ul style="padding:5px;padding-left:16px;">
38931              * <li>grid - This grid</li>
38932              * <li>record - The record being edited</li>
38933              * <li>field - The field name being edited</li>
38934              * <li>value - The value for the field being edited.</li>
38935              * <li>row - The grid row index</li>
38936              * <li>column - The grid column index</li>
38937              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
38938              * </ul>
38939              * @param {Object} e An edit event (see above for description)
38940              */
38941             "beforeedit" : true,
38942             /**
38943              * @event afteredit
38944              * Fires after a cell is edited. <br />
38945              * <ul style="padding:5px;padding-left:16px;">
38946              * <li>grid - This grid</li>
38947              * <li>record - The record being edited</li>
38948              * <li>field - The field name being edited</li>
38949              * <li>value - The value being set</li>
38950              * <li>originalValue - The original value for the field, before the edit.</li>
38951              * <li>row - The grid row index</li>
38952              * <li>column - The grid column index</li>
38953              * </ul>
38954              * @param {Object} e An edit event (see above for description)
38955              */
38956             "afteredit" : true,
38957             /**
38958              * @event validateedit
38959              * Fires after a cell is edited, but before the value is set in the record. 
38960          * You can use this to modify the value being set in the field, Return false
38961              * to cancel the change. The edit event object has the following properties <br />
38962              * <ul style="padding:5px;padding-left:16px;">
38963          * <li>editor - This editor</li>
38964              * <li>grid - This grid</li>
38965              * <li>record - The record being edited</li>
38966              * <li>field - The field name being edited</li>
38967              * <li>value - The value being set</li>
38968              * <li>originalValue - The original value for the field, before the edit.</li>
38969              * <li>row - The grid row index</li>
38970              * <li>column - The grid column index</li>
38971              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
38972              * </ul>
38973              * @param {Object} e An edit event (see above for description)
38974              */
38975             "validateedit" : true
38976         });
38977     this.on("bodyscroll", this.stopEditing,  this);
38978     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
38979 };
38980
38981 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
38982     /**
38983      * @cfg {Number} clicksToEdit
38984      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
38985      */
38986     clicksToEdit: 2,
38987
38988     // private
38989     isEditor : true,
38990     // private
38991     trackMouseOver: false, // causes very odd FF errors
38992
38993     onCellDblClick : function(g, row, col){
38994         this.startEditing(row, col);
38995     },
38996
38997     onEditComplete : function(ed, value, startValue){
38998         this.editing = false;
38999         this.activeEditor = null;
39000         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
39001         var r = ed.record;
39002         var field = this.colModel.getDataIndex(ed.col);
39003         var e = {
39004             grid: this,
39005             record: r,
39006             field: field,
39007             originalValue: startValue,
39008             value: value,
39009             row: ed.row,
39010             column: ed.col,
39011             cancel:false,
39012             editor: ed
39013         };
39014         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
39015         cell.show();
39016           
39017         if(String(value) !== String(startValue)){
39018             
39019             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
39020                 r.set(field, e.value);
39021                 // if we are dealing with a combo box..
39022                 // then we also set the 'name' colum to be the displayField
39023                 if (ed.field.displayField && ed.field.name) {
39024                     r.set(ed.field.name, ed.field.el.dom.value);
39025                 }
39026                 
39027                 delete e.cancel; //?? why!!!
39028                 this.fireEvent("afteredit", e);
39029             }
39030         } else {
39031             this.fireEvent("afteredit", e); // always fire it!
39032         }
39033         this.view.focusCell(ed.row, ed.col);
39034     },
39035
39036     /**
39037      * Starts editing the specified for the specified row/column
39038      * @param {Number} rowIndex
39039      * @param {Number} colIndex
39040      */
39041     startEditing : function(row, col){
39042         this.stopEditing();
39043         if(this.colModel.isCellEditable(col, row)){
39044             this.view.ensureVisible(row, col, true);
39045           
39046             var r = this.dataSource.getAt(row);
39047             var field = this.colModel.getDataIndex(col);
39048             var cell = Roo.get(this.view.getCell(row,col));
39049             var e = {
39050                 grid: this,
39051                 record: r,
39052                 field: field,
39053                 value: r.data[field],
39054                 row: row,
39055                 column: col,
39056                 cancel:false 
39057             };
39058             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
39059                 this.editing = true;
39060                 var ed = this.colModel.getCellEditor(col, row);
39061                 
39062                 if (!ed) {
39063                     return;
39064                 }
39065                 if(!ed.rendered){
39066                     ed.render(ed.parentEl || document.body);
39067                 }
39068                 ed.field.reset();
39069                
39070                 cell.hide();
39071                 
39072                 (function(){ // complex but required for focus issues in safari, ie and opera
39073                     ed.row = row;
39074                     ed.col = col;
39075                     ed.record = r;
39076                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
39077                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
39078                     this.activeEditor = ed;
39079                     var v = r.data[field];
39080                     ed.startEdit(this.view.getCell(row, col), v);
39081                     // combo's with 'displayField and name set
39082                     if (ed.field.displayField && ed.field.name) {
39083                         ed.field.el.dom.value = r.data[ed.field.name];
39084                     }
39085                     
39086                     
39087                 }).defer(50, this);
39088             }
39089         }
39090     },
39091         
39092     /**
39093      * Stops any active editing
39094      */
39095     stopEditing : function(){
39096         if(this.activeEditor){
39097             this.activeEditor.completeEdit();
39098         }
39099         this.activeEditor = null;
39100     },
39101         
39102          /**
39103      * Called to get grid's drag proxy text, by default returns this.ddText.
39104      * @return {String}
39105      */
39106     getDragDropText : function(){
39107         var count = this.selModel.getSelectedCell() ? 1 : 0;
39108         return String.format(this.ddText, count, count == 1 ? '' : 's');
39109     }
39110         
39111 });/*
39112  * Based on:
39113  * Ext JS Library 1.1.1
39114  * Copyright(c) 2006-2007, Ext JS, LLC.
39115  *
39116  * Originally Released Under LGPL - original licence link has changed is not relivant.
39117  *
39118  * Fork - LGPL
39119  * <script type="text/javascript">
39120  */
39121
39122 // private - not really -- you end up using it !
39123 // This is a support class used internally by the Grid components
39124
39125 /**
39126  * @class Roo.grid.GridEditor
39127  * @extends Roo.Editor
39128  * Class for creating and editable grid elements.
39129  * @param {Object} config any settings (must include field)
39130  */
39131 Roo.grid.GridEditor = function(field, config){
39132     if (!config && field.field) {
39133         config = field;
39134         field = Roo.factory(config.field, Roo.form);
39135     }
39136     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
39137     field.monitorTab = false;
39138 };
39139
39140 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
39141     
39142     /**
39143      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
39144      */
39145     
39146     alignment: "tl-tl",
39147     autoSize: "width",
39148     hideEl : false,
39149     cls: "x-small-editor x-grid-editor",
39150     shim:false,
39151     shadow:"frame"
39152 });/*
39153  * Based on:
39154  * Ext JS Library 1.1.1
39155  * Copyright(c) 2006-2007, Ext JS, LLC.
39156  *
39157  * Originally Released Under LGPL - original licence link has changed is not relivant.
39158  *
39159  * Fork - LGPL
39160  * <script type="text/javascript">
39161  */
39162   
39163
39164   
39165 Roo.grid.PropertyRecord = Roo.data.Record.create([
39166     {name:'name',type:'string'},  'value'
39167 ]);
39168
39169
39170 Roo.grid.PropertyStore = function(grid, source){
39171     this.grid = grid;
39172     this.store = new Roo.data.Store({
39173         recordType : Roo.grid.PropertyRecord
39174     });
39175     this.store.on('update', this.onUpdate,  this);
39176     if(source){
39177         this.setSource(source);
39178     }
39179     Roo.grid.PropertyStore.superclass.constructor.call(this);
39180 };
39181
39182
39183
39184 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
39185     setSource : function(o){
39186         this.source = o;
39187         this.store.removeAll();
39188         var data = [];
39189         for(var k in o){
39190             if(this.isEditableValue(o[k])){
39191                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
39192             }
39193         }
39194         this.store.loadRecords({records: data}, {}, true);
39195     },
39196
39197     onUpdate : function(ds, record, type){
39198         if(type == Roo.data.Record.EDIT){
39199             var v = record.data['value'];
39200             var oldValue = record.modified['value'];
39201             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
39202                 this.source[record.id] = v;
39203                 record.commit();
39204                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
39205             }else{
39206                 record.reject();
39207             }
39208         }
39209     },
39210
39211     getProperty : function(row){
39212        return this.store.getAt(row);
39213     },
39214
39215     isEditableValue: function(val){
39216         if(val && val instanceof Date){
39217             return true;
39218         }else if(typeof val == 'object' || typeof val == 'function'){
39219             return false;
39220         }
39221         return true;
39222     },
39223
39224     setValue : function(prop, value){
39225         this.source[prop] = value;
39226         this.store.getById(prop).set('value', value);
39227     },
39228
39229     getSource : function(){
39230         return this.source;
39231     }
39232 });
39233
39234 Roo.grid.PropertyColumnModel = function(grid, store){
39235     this.grid = grid;
39236     var g = Roo.grid;
39237     g.PropertyColumnModel.superclass.constructor.call(this, [
39238         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
39239         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
39240     ]);
39241     this.store = store;
39242     this.bselect = Roo.DomHelper.append(document.body, {
39243         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
39244             {tag: 'option', value: 'true', html: 'true'},
39245             {tag: 'option', value: 'false', html: 'false'}
39246         ]
39247     });
39248     Roo.id(this.bselect);
39249     var f = Roo.form;
39250     this.editors = {
39251         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
39252         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
39253         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
39254         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
39255         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
39256     };
39257     this.renderCellDelegate = this.renderCell.createDelegate(this);
39258     this.renderPropDelegate = this.renderProp.createDelegate(this);
39259 };
39260
39261 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
39262     
39263     
39264     nameText : 'Name',
39265     valueText : 'Value',
39266     
39267     dateFormat : 'm/j/Y',
39268     
39269     
39270     renderDate : function(dateVal){
39271         return dateVal.dateFormat(this.dateFormat);
39272     },
39273
39274     renderBool : function(bVal){
39275         return bVal ? 'true' : 'false';
39276     },
39277
39278     isCellEditable : function(colIndex, rowIndex){
39279         return colIndex == 1;
39280     },
39281
39282     getRenderer : function(col){
39283         return col == 1 ?
39284             this.renderCellDelegate : this.renderPropDelegate;
39285     },
39286
39287     renderProp : function(v){
39288         return this.getPropertyName(v);
39289     },
39290
39291     renderCell : function(val){
39292         var rv = val;
39293         if(val instanceof Date){
39294             rv = this.renderDate(val);
39295         }else if(typeof val == 'boolean'){
39296             rv = this.renderBool(val);
39297         }
39298         return Roo.util.Format.htmlEncode(rv);
39299     },
39300
39301     getPropertyName : function(name){
39302         var pn = this.grid.propertyNames;
39303         return pn && pn[name] ? pn[name] : name;
39304     },
39305
39306     getCellEditor : function(colIndex, rowIndex){
39307         var p = this.store.getProperty(rowIndex);
39308         var n = p.data['name'], val = p.data['value'];
39309         
39310         if(typeof(this.grid.customEditors[n]) == 'string'){
39311             return this.editors[this.grid.customEditors[n]];
39312         }
39313         if(typeof(this.grid.customEditors[n]) != 'undefined'){
39314             return this.grid.customEditors[n];
39315         }
39316         if(val instanceof Date){
39317             return this.editors['date'];
39318         }else if(typeof val == 'number'){
39319             return this.editors['number'];
39320         }else if(typeof val == 'boolean'){
39321             return this.editors['boolean'];
39322         }else{
39323             return this.editors['string'];
39324         }
39325     }
39326 });
39327
39328 /**
39329  * @class Roo.grid.PropertyGrid
39330  * @extends Roo.grid.EditorGrid
39331  * This class represents the  interface of a component based property grid control.
39332  * <br><br>Usage:<pre><code>
39333  var grid = new Roo.grid.PropertyGrid("my-container-id", {
39334       
39335  });
39336  // set any options
39337  grid.render();
39338  * </code></pre>
39339   
39340  * @constructor
39341  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
39342  * The container MUST have some type of size defined for the grid to fill. The container will be
39343  * automatically set to position relative if it isn't already.
39344  * @param {Object} config A config object that sets properties on this grid.
39345  */
39346 Roo.grid.PropertyGrid = function(container, config){
39347     config = config || {};
39348     var store = new Roo.grid.PropertyStore(this);
39349     this.store = store;
39350     var cm = new Roo.grid.PropertyColumnModel(this, store);
39351     store.store.sort('name', 'ASC');
39352     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
39353         ds: store.store,
39354         cm: cm,
39355         enableColLock:false,
39356         enableColumnMove:false,
39357         stripeRows:false,
39358         trackMouseOver: false,
39359         clicksToEdit:1
39360     }, config));
39361     this.getGridEl().addClass('x-props-grid');
39362     this.lastEditRow = null;
39363     this.on('columnresize', this.onColumnResize, this);
39364     this.addEvents({
39365          /**
39366              * @event beforepropertychange
39367              * Fires before a property changes (return false to stop?)
39368              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
39369              * @param {String} id Record Id
39370              * @param {String} newval New Value
39371          * @param {String} oldval Old Value
39372              */
39373         "beforepropertychange": true,
39374         /**
39375              * @event propertychange
39376              * Fires after a property changes
39377              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
39378              * @param {String} id Record Id
39379              * @param {String} newval New Value
39380          * @param {String} oldval Old Value
39381              */
39382         "propertychange": true
39383     });
39384     this.customEditors = this.customEditors || {};
39385 };
39386 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
39387     
39388      /**
39389      * @cfg {Object} customEditors map of colnames=> custom editors.
39390      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
39391      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
39392      * false disables editing of the field.
39393          */
39394     
39395       /**
39396      * @cfg {Object} propertyNames map of property Names to their displayed value
39397          */
39398     
39399     render : function(){
39400         Roo.grid.PropertyGrid.superclass.render.call(this);
39401         this.autoSize.defer(100, this);
39402     },
39403
39404     autoSize : function(){
39405         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
39406         if(this.view){
39407             this.view.fitColumns();
39408         }
39409     },
39410
39411     onColumnResize : function(){
39412         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
39413         this.autoSize();
39414     },
39415     /**
39416      * Sets the data for the Grid
39417      * accepts a Key => Value object of all the elements avaiable.
39418      * @param {Object} data  to appear in grid.
39419      */
39420     setSource : function(source){
39421         this.store.setSource(source);
39422         //this.autoSize();
39423     },
39424     /**
39425      * Gets all the data from the grid.
39426      * @return {Object} data  data stored in grid
39427      */
39428     getSource : function(){
39429         return this.store.getSource();
39430     }
39431 });/*
39432  * Based on:
39433  * Ext JS Library 1.1.1
39434  * Copyright(c) 2006-2007, Ext JS, LLC.
39435  *
39436  * Originally Released Under LGPL - original licence link has changed is not relivant.
39437  *
39438  * Fork - LGPL
39439  * <script type="text/javascript">
39440  */
39441  
39442 /**
39443  * @class Roo.LoadMask
39444  * A simple utility class for generically masking elements while loading data.  If the element being masked has
39445  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
39446  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
39447  * element's UpdateManager load indicator and will be destroyed after the initial load.
39448  * @constructor
39449  * Create a new LoadMask
39450  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
39451  * @param {Object} config The config object
39452  */
39453 Roo.LoadMask = function(el, config){
39454     this.el = Roo.get(el);
39455     Roo.apply(this, config);
39456     if(this.store){
39457         this.store.on('beforeload', this.onBeforeLoad, this);
39458         this.store.on('load', this.onLoad, this);
39459         this.store.on('loadexception', this.onLoadException, this);
39460         this.removeMask = false;
39461     }else{
39462         var um = this.el.getUpdateManager();
39463         um.showLoadIndicator = false; // disable the default indicator
39464         um.on('beforeupdate', this.onBeforeLoad, this);
39465         um.on('update', this.onLoad, this);
39466         um.on('failure', this.onLoad, this);
39467         this.removeMask = true;
39468     }
39469 };
39470
39471 Roo.LoadMask.prototype = {
39472     /**
39473      * @cfg {Boolean} removeMask
39474      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
39475      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
39476      */
39477     /**
39478      * @cfg {String} msg
39479      * The text to display in a centered loading message box (defaults to 'Loading...')
39480      */
39481     msg : 'Loading...',
39482     /**
39483      * @cfg {String} msgCls
39484      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
39485      */
39486     msgCls : 'x-mask-loading',
39487
39488     /**
39489      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
39490      * @type Boolean
39491      */
39492     disabled: false,
39493
39494     /**
39495      * Disables the mask to prevent it from being displayed
39496      */
39497     disable : function(){
39498        this.disabled = true;
39499     },
39500
39501     /**
39502      * Enables the mask so that it can be displayed
39503      */
39504     enable : function(){
39505         this.disabled = false;
39506     },
39507     
39508     onLoadException : function()
39509     {
39510         Roo.log(arguments);
39511         
39512         if (typeof(arguments[3]) != 'undefined') {
39513             Roo.MessageBox.alert("Error loading",arguments[3]);
39514         } 
39515         /*
39516         try {
39517             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
39518                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
39519             }   
39520         } catch(e) {
39521             
39522         }
39523         */
39524     
39525         
39526         
39527         this.el.unmask(this.removeMask);
39528     },
39529     // private
39530     onLoad : function()
39531     {
39532         this.el.unmask(this.removeMask);
39533     },
39534
39535     // private
39536     onBeforeLoad : function(){
39537         if(!this.disabled){
39538             this.el.mask(this.msg, this.msgCls);
39539         }
39540     },
39541
39542     // private
39543     destroy : function(){
39544         if(this.store){
39545             this.store.un('beforeload', this.onBeforeLoad, this);
39546             this.store.un('load', this.onLoad, this);
39547             this.store.un('loadexception', this.onLoadException, this);
39548         }else{
39549             var um = this.el.getUpdateManager();
39550             um.un('beforeupdate', this.onBeforeLoad, this);
39551             um.un('update', this.onLoad, this);
39552             um.un('failure', this.onLoad, this);
39553         }
39554     }
39555 };/*
39556  * Based on:
39557  * Ext JS Library 1.1.1
39558  * Copyright(c) 2006-2007, Ext JS, LLC.
39559  *
39560  * Originally Released Under LGPL - original licence link has changed is not relivant.
39561  *
39562  * Fork - LGPL
39563  * <script type="text/javascript">
39564  */
39565
39566
39567 /**
39568  * @class Roo.XTemplate
39569  * @extends Roo.Template
39570  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
39571 <pre><code>
39572 var t = new Roo.XTemplate(
39573         '&lt;select name="{name}"&gt;',
39574                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
39575         '&lt;/select&gt;'
39576 );
39577  
39578 // then append, applying the master template values
39579  </code></pre>
39580  *
39581  * Supported features:
39582  *
39583  *  Tags:
39584
39585 <pre><code>
39586       {a_variable} - output encoded.
39587       {a_variable.format:("Y-m-d")} - call a method on the variable
39588       {a_variable:raw} - unencoded output
39589       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
39590       {a_variable:this.method_on_template(...)} - call a method on the template object.
39591  
39592 </code></pre>
39593  *  The tpl tag:
39594 <pre><code>
39595         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
39596         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
39597         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
39598         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
39599   
39600         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
39601         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
39602 </code></pre>
39603  *      
39604  */
39605 Roo.XTemplate = function()
39606 {
39607     Roo.XTemplate.superclass.constructor.apply(this, arguments);
39608     if (this.html) {
39609         this.compile();
39610     }
39611 };
39612
39613
39614 Roo.extend(Roo.XTemplate, Roo.Template, {
39615
39616     /**
39617      * The various sub templates
39618      */
39619     tpls : false,
39620     /**
39621      *
39622      * basic tag replacing syntax
39623      * WORD:WORD()
39624      *
39625      * // you can fake an object call by doing this
39626      *  x.t:(test,tesT) 
39627      * 
39628      */
39629     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
39630
39631     /**
39632      * compile the template
39633      *
39634      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
39635      *
39636      */
39637     compile: function()
39638     {
39639         var s = this.html;
39640      
39641         s = ['<tpl>', s, '</tpl>'].join('');
39642     
39643         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
39644             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
39645             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
39646             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
39647             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
39648             m,
39649             id     = 0,
39650             tpls   = [];
39651     
39652         while(true == !!(m = s.match(re))){
39653             var forMatch   = m[0].match(nameRe),
39654                 ifMatch   = m[0].match(ifRe),
39655                 execMatch   = m[0].match(execRe),
39656                 namedMatch   = m[0].match(namedRe),
39657                 
39658                 exp  = null, 
39659                 fn   = null,
39660                 exec = null,
39661                 name = forMatch && forMatch[1] ? forMatch[1] : '';
39662                 
39663             if (ifMatch) {
39664                 // if - puts fn into test..
39665                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
39666                 if(exp){
39667                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
39668                 }
39669             }
39670             
39671             if (execMatch) {
39672                 // exec - calls a function... returns empty if true is  returned.
39673                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
39674                 if(exp){
39675                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
39676                 }
39677             }
39678             
39679             
39680             if (name) {
39681                 // for = 
39682                 switch(name){
39683                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
39684                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
39685                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
39686                 }
39687             }
39688             var uid = namedMatch ? namedMatch[1] : id;
39689             
39690             
39691             tpls.push({
39692                 id:     namedMatch ? namedMatch[1] : id,
39693                 target: name,
39694                 exec:   exec,
39695                 test:   fn,
39696                 body:   m[1] || ''
39697             });
39698             if (namedMatch) {
39699                 s = s.replace(m[0], '');
39700             } else { 
39701                 s = s.replace(m[0], '{xtpl'+ id + '}');
39702             }
39703             ++id;
39704         }
39705         this.tpls = [];
39706         for(var i = tpls.length-1; i >= 0; --i){
39707             this.compileTpl(tpls[i]);
39708             this.tpls[tpls[i].id] = tpls[i];
39709         }
39710         this.master = tpls[tpls.length-1];
39711         return this;
39712     },
39713     /**
39714      * same as applyTemplate, except it's done to one of the subTemplates
39715      * when using named templates, you can do:
39716      *
39717      * var str = pl.applySubTemplate('your-name', values);
39718      *
39719      * 
39720      * @param {Number} id of the template
39721      * @param {Object} values to apply to template
39722      * @param {Object} parent (normaly the instance of this object)
39723      */
39724     applySubTemplate : function(id, values, parent)
39725     {
39726         
39727         
39728         var t = this.tpls[id];
39729         
39730         
39731         try { 
39732             if(t.test && !t.test.call(this, values, parent)){
39733                 return '';
39734             }
39735         } catch(e) {
39736             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
39737             Roo.log(e.toString());
39738             Roo.log(t.test);
39739             return ''
39740         }
39741         try { 
39742             
39743             if(t.exec && t.exec.call(this, values, parent)){
39744                 return '';
39745             }
39746         } catch(e) {
39747             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
39748             Roo.log(e.toString());
39749             Roo.log(t.exec);
39750             return ''
39751         }
39752         try {
39753             var vs = t.target ? t.target.call(this, values, parent) : values;
39754             parent = t.target ? values : parent;
39755             if(t.target && vs instanceof Array){
39756                 var buf = [];
39757                 for(var i = 0, len = vs.length; i < len; i++){
39758                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
39759                 }
39760                 return buf.join('');
39761             }
39762             return t.compiled.call(this, vs, parent);
39763         } catch (e) {
39764             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
39765             Roo.log(e.toString());
39766             Roo.log(t.compiled);
39767             return '';
39768         }
39769     },
39770
39771     compileTpl : function(tpl)
39772     {
39773         var fm = Roo.util.Format;
39774         var useF = this.disableFormats !== true;
39775         var sep = Roo.isGecko ? "+" : ",";
39776         var undef = function(str) {
39777             Roo.log("Property not found :"  + str);
39778             return '';
39779         };
39780         
39781         var fn = function(m, name, format, args)
39782         {
39783             //Roo.log(arguments);
39784             args = args ? args.replace(/\\'/g,"'") : args;
39785             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
39786             if (typeof(format) == 'undefined') {
39787                 format= 'htmlEncode';
39788             }
39789             if (format == 'raw' ) {
39790                 format = false;
39791             }
39792             
39793             if(name.substr(0, 4) == 'xtpl'){
39794                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
39795             }
39796             
39797             // build an array of options to determine if value is undefined..
39798             
39799             // basically get 'xxxx.yyyy' then do
39800             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
39801             //    (function () { Roo.log("Property not found"); return ''; })() :
39802             //    ......
39803             
39804             var udef_ar = [];
39805             var lookfor = '';
39806             Roo.each(name.split('.'), function(st) {
39807                 lookfor += (lookfor.length ? '.': '') + st;
39808                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
39809             });
39810             
39811             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
39812             
39813             
39814             if(format && useF){
39815                 
39816                 args = args ? ',' + args : "";
39817                  
39818                 if(format.substr(0, 5) != "this."){
39819                     format = "fm." + format + '(';
39820                 }else{
39821                     format = 'this.call("'+ format.substr(5) + '", ';
39822                     args = ", values";
39823                 }
39824                 
39825                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
39826             }
39827              
39828             if (args.length) {
39829                 // called with xxyx.yuu:(test,test)
39830                 // change to ()
39831                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
39832             }
39833             // raw.. - :raw modifier..
39834             return "'"+ sep + udef_st  + name + ")"+sep+"'";
39835             
39836         };
39837         var body;
39838         // branched to use + in gecko and [].join() in others
39839         if(Roo.isGecko){
39840             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
39841                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
39842                     "';};};";
39843         }else{
39844             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
39845             body.push(tpl.body.replace(/(\r\n|\n)/g,
39846                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
39847             body.push("'].join('');};};");
39848             body = body.join('');
39849         }
39850         
39851         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
39852        
39853         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
39854         eval(body);
39855         
39856         return this;
39857     },
39858
39859     applyTemplate : function(values){
39860         return this.master.compiled.call(this, values, {});
39861         //var s = this.subs;
39862     },
39863
39864     apply : function(){
39865         return this.applyTemplate.apply(this, arguments);
39866     }
39867
39868  });
39869
39870 Roo.XTemplate.from = function(el){
39871     el = Roo.getDom(el);
39872     return new Roo.XTemplate(el.value || el.innerHTML);
39873 };/*
39874  * Original code for Roojs - LGPL
39875  * <script type="text/javascript">
39876  */
39877  
39878 /**
39879  * @class Roo.XComponent
39880  * A delayed Element creator...
39881  * Or a way to group chunks of interface together.
39882  * 
39883  * Mypart.xyx = new Roo.XComponent({
39884
39885     parent : 'Mypart.xyz', // empty == document.element.!!
39886     order : '001',
39887     name : 'xxxx'
39888     region : 'xxxx'
39889     disabled : function() {} 
39890      
39891     tree : function() { // return an tree of xtype declared components
39892         var MODULE = this;
39893         return 
39894         {
39895             xtype : 'NestedLayoutPanel',
39896             // technicall
39897         }
39898      ]
39899  *})
39900  *
39901  *
39902  * It can be used to build a big heiracy, with parent etc.
39903  * or you can just use this to render a single compoent to a dom element
39904  * MYPART.render(Roo.Element | String(id) | dom_element )
39905  * 
39906  * @extends Roo.util.Observable
39907  * @constructor
39908  * @param cfg {Object} configuration of component
39909  * 
39910  */
39911 Roo.XComponent = function(cfg) {
39912     Roo.apply(this, cfg);
39913     this.addEvents({ 
39914         /**
39915              * @event built
39916              * Fires when this the componnt is built
39917              * @param {Roo.XComponent} c the component
39918              */
39919         'built' : true
39920         
39921     });
39922     this.region = this.region || 'center'; // default..
39923     Roo.XComponent.register(this);
39924     this.modules = false;
39925     this.el = false; // where the layout goes..
39926     
39927     
39928 }
39929 Roo.extend(Roo.XComponent, Roo.util.Observable, {
39930     /**
39931      * @property el
39932      * The created element (with Roo.factory())
39933      * @type {Roo.Layout}
39934      */
39935     el  : false,
39936     
39937     /**
39938      * @property el
39939      * for BC  - use el in new code
39940      * @type {Roo.Layout}
39941      */
39942     panel : false,
39943     
39944     /**
39945      * @property layout
39946      * for BC  - use el in new code
39947      * @type {Roo.Layout}
39948      */
39949     layout : false,
39950     
39951      /**
39952      * @cfg {Function|boolean} disabled
39953      * If this module is disabled by some rule, return true from the funtion
39954      */
39955     disabled : false,
39956     
39957     /**
39958      * @cfg {String} parent 
39959      * Name of parent element which it get xtype added to..
39960      */
39961     parent: false,
39962     
39963     /**
39964      * @cfg {String} order
39965      * Used to set the order in which elements are created (usefull for multiple tabs)
39966      */
39967     
39968     order : false,
39969     /**
39970      * @cfg {String} name
39971      * String to display while loading.
39972      */
39973     name : false,
39974     /**
39975      * @cfg {String} region
39976      * Region to render component to (defaults to center)
39977      */
39978     region : 'center',
39979     
39980     /**
39981      * @cfg {Array} items
39982      * A single item array - the first element is the root of the tree..
39983      * It's done this way to stay compatible with the Xtype system...
39984      */
39985     items : false,
39986     
39987     /**
39988      * @property _tree
39989      * The method that retuns the tree of parts that make up this compoennt 
39990      * @type {function}
39991      */
39992     _tree  : false,
39993     
39994      /**
39995      * render
39996      * render element to dom or tree
39997      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
39998      */
39999     
40000     render : function(el)
40001     {
40002         
40003         el = el || false;
40004         var hp = this.parent ? 1 : 0;
40005         
40006         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
40007             // if parent is a '#.....' string, then let's use that..
40008             var ename = this.parent.substr(1)
40009             this.parent = false;
40010             el = Roo.get(ename);
40011             if (!el) {
40012                 Roo.log("Warning - element can not be found :#" + ename );
40013                 return;
40014             }
40015         }
40016         
40017         
40018         if (!this.parent) {
40019             
40020             el = el ? Roo.get(el) : false;      
40021             
40022             // it's a top level one..
40023             this.parent =  {
40024                 el : new Roo.BorderLayout(el || document.body, {
40025                 
40026                      center: {
40027                          titlebar: false,
40028                          autoScroll:false,
40029                          closeOnTab: true,
40030                          tabPosition: 'top',
40031                           //resizeTabs: true,
40032                          alwaysShowTabs: el && hp? false :  true,
40033                          hideTabs: el || !hp ? true :  false,
40034                          minTabWidth: 140
40035                      }
40036                  })
40037             }
40038         }
40039         
40040                 if (!this.parent.el) {
40041                         // probably an old style ctor, which has been disabled.
40042                         return;
40043                         
40044                 }
40045                 // The 'tree' method is  '_tree now' 
40046             
40047         var tree = this._tree ? this._tree() : this.tree();
40048         tree.region = tree.region || this.region;
40049         this.el = this.parent.el.addxtype(tree);
40050         this.fireEvent('built', this);
40051         
40052         this.panel = this.el;
40053         this.layout = this.panel.layout;
40054                 this.parentLayout = this.parent.layout  || false;  
40055          
40056     }
40057     
40058 });
40059
40060 Roo.apply(Roo.XComponent, {
40061     /**
40062      * @property  hideProgress
40063      * true to disable the building progress bar.. usefull on single page renders.
40064      * @type Boolean
40065      */
40066     hideProgress : false,
40067     /**
40068      * @property  buildCompleted
40069      * True when the builder has completed building the interface.
40070      * @type Boolean
40071      */
40072     buildCompleted : false,
40073      
40074     /**
40075      * @property  topModule
40076      * the upper most module - uses document.element as it's constructor.
40077      * @type Object
40078      */
40079      
40080     topModule  : false,
40081       
40082     /**
40083      * @property  modules
40084      * array of modules to be created by registration system.
40085      * @type {Array} of Roo.XComponent
40086      */
40087     
40088     modules : [],
40089     /**
40090      * @property  elmodules
40091      * array of modules to be created by which use #ID 
40092      * @type {Array} of Roo.XComponent
40093      */
40094      
40095     elmodules : [],
40096
40097     
40098     /**
40099      * Register components to be built later.
40100      *
40101      * This solves the following issues
40102      * - Building is not done on page load, but after an authentication process has occured.
40103      * - Interface elements are registered on page load
40104      * - Parent Interface elements may not be loaded before child, so this handles that..
40105      * 
40106      *
40107      * example:
40108      * 
40109      * MyApp.register({
40110           order : '000001',
40111           module : 'Pman.Tab.projectMgr',
40112           region : 'center',
40113           parent : 'Pman.layout',
40114           disabled : false,  // or use a function..
40115         })
40116      
40117      * * @param {Object} details about module
40118      */
40119     register : function(obj) {
40120                 
40121         Roo.XComponent.event.fireEvent('register', obj);
40122         switch(typeof(obj.disabled) ) {
40123                 
40124             case 'undefined':
40125                 break;
40126             
40127             case 'function':
40128                 if ( obj.disabled() ) {
40129                         return;
40130                 }
40131                 break;
40132             
40133             default:
40134                 if (obj.disabled) {
40135                         return;
40136                 }
40137                 break;
40138         }
40139                 
40140         this.modules.push(obj);
40141          
40142     },
40143     /**
40144      * convert a string to an object..
40145      * eg. 'AAA.BBB' -> finds AAA.BBB
40146
40147      */
40148     
40149     toObject : function(str)
40150     {
40151         if (!str || typeof(str) == 'object') {
40152             return str;
40153         }
40154         if (str.substring(0,1) == '#') {
40155             return str;
40156         }
40157
40158         var ar = str.split('.');
40159         var rt, o;
40160         rt = ar.shift();
40161             /** eval:var:o */
40162         try {
40163             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
40164         } catch (e) {
40165             throw "Module not found : " + str;
40166         }
40167         
40168         if (o === false) {
40169             throw "Module not found : " + str;
40170         }
40171         Roo.each(ar, function(e) {
40172             if (typeof(o[e]) == 'undefined') {
40173                 throw "Module not found : " + str;
40174             }
40175             o = o[e];
40176         });
40177         
40178         return o;
40179         
40180     },
40181     
40182     
40183     /**
40184      * move modules into their correct place in the tree..
40185      * 
40186      */
40187     preBuild : function ()
40188     {
40189         var _t = this;
40190         Roo.each(this.modules , function (obj)
40191         {
40192             Roo.XComponent.event.fireEvent('beforebuild', obj);
40193             
40194             var opar = obj.parent;
40195             try { 
40196                 obj.parent = this.toObject(opar);
40197             } catch(e) {
40198                 Roo.log("parent:toObject failed: " + e.toString());
40199                 return;
40200             }
40201             
40202             if (!obj.parent) {
40203                 Roo.debug && Roo.log("GOT top level module");
40204                 Roo.debug && Roo.log(obj);
40205                 obj.modules = new Roo.util.MixedCollection(false, 
40206                     function(o) { return o.order + '' }
40207                 );
40208                 this.topModule = obj;
40209                 return;
40210             }
40211                         // parent is a string (usually a dom element name..)
40212             if (typeof(obj.parent) == 'string') {
40213                 this.elmodules.push(obj);
40214                 return;
40215             }
40216             if (obj.parent.constructor != Roo.XComponent) {
40217                 Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
40218             }
40219             if (!obj.parent.modules) {
40220                 obj.parent.modules = new Roo.util.MixedCollection(false, 
40221                     function(o) { return o.order + '' }
40222                 );
40223             }
40224             if (obj.parent.disabled) {
40225                 obj.disabled = true;
40226             }
40227             obj.parent.modules.add(obj);
40228         }, this);
40229     },
40230     
40231      /**
40232      * make a list of modules to build.
40233      * @return {Array} list of modules. 
40234      */ 
40235     
40236     buildOrder : function()
40237     {
40238         var _this = this;
40239         var cmp = function(a,b) {   
40240             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
40241         };
40242         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
40243             throw "No top level modules to build";
40244         }
40245         
40246         // make a flat list in order of modules to build.
40247         var mods = this.topModule ? [ this.topModule ] : [];
40248                 
40249         
40250         // elmodules (is a list of DOM based modules )
40251         Roo.each(this.elmodules, function(e) {
40252             mods.push(e);
40253             if (!this.topModule &&
40254                 typeof(e.parent) == 'string' &&
40255                 e.parent.substring(0,1) == '#' &&
40256                 Roo.get(e.parent.substr(1))
40257                ) {
40258                 
40259                 _this.topModule = e;
40260             }
40261             
40262         });
40263
40264         
40265         // add modules to their parents..
40266         var addMod = function(m) {
40267             Roo.debug && Roo.log("build Order: add: " + m.name);
40268                 
40269             mods.push(m);
40270             if (m.modules && !m.disabled) {
40271                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
40272                 m.modules.keySort('ASC',  cmp );
40273                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
40274     
40275                 m.modules.each(addMod);
40276             } else {
40277                 Roo.debug && Roo.log("build Order: no child modules");
40278             }
40279             // not sure if this is used any more..
40280             if (m.finalize) {
40281                 m.finalize.name = m.name + " (clean up) ";
40282                 mods.push(m.finalize);
40283             }
40284             
40285         }
40286         if (this.topModule && this.topModule.modules) { 
40287             this.topModule.modules.keySort('ASC',  cmp );
40288             this.topModule.modules.each(addMod);
40289         } 
40290         return mods;
40291     },
40292     
40293      /**
40294      * Build the registered modules.
40295      * @param {Object} parent element.
40296      * @param {Function} optional method to call after module has been added.
40297      * 
40298      */ 
40299    
40300     build : function() 
40301     {
40302         
40303         this.preBuild();
40304         var mods = this.buildOrder();
40305       
40306         //this.allmods = mods;
40307         //Roo.debug && Roo.log(mods);
40308         //return;
40309         if (!mods.length) { // should not happen
40310             throw "NO modules!!!";
40311         }
40312         
40313         
40314         var msg = "Building Interface...";
40315         // flash it up as modal - so we store the mask!?
40316         if (!this.hideProgress) {
40317             Roo.MessageBox.show({ title: 'loading' });
40318             Roo.MessageBox.show({
40319                title: "Please wait...",
40320                msg: msg,
40321                width:450,
40322                progress:true,
40323                closable:false,
40324                modal: false
40325               
40326             });
40327         }
40328         var total = mods.length;
40329         
40330         var _this = this;
40331         var progressRun = function() {
40332             if (!mods.length) {
40333                 Roo.debug && Roo.log('hide?');
40334                 if (!this.hideProgress) {
40335                     Roo.MessageBox.hide();
40336                 }
40337                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
40338                 
40339                 // THE END...
40340                 return false;   
40341             }
40342             
40343             var m = mods.shift();
40344             
40345             
40346             Roo.debug && Roo.log(m);
40347             // not sure if this is supported any more.. - modules that are are just function
40348             if (typeof(m) == 'function') { 
40349                 m.call(this);
40350                 return progressRun.defer(10, _this);
40351             } 
40352             
40353             
40354             msg = "Building Interface " + (total  - mods.length) + 
40355                     " of " + total + 
40356                     (m.name ? (' - ' + m.name) : '');
40357                         Roo.debug && Roo.log(msg);
40358             if (!this.hideProgress) { 
40359                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
40360             }
40361             
40362          
40363             // is the module disabled?
40364             var disabled = (typeof(m.disabled) == 'function') ?
40365                 m.disabled.call(m.module.disabled) : m.disabled;    
40366             
40367             
40368             if (disabled) {
40369                 return progressRun(); // we do not update the display!
40370             }
40371             
40372             // now build 
40373             
40374                         
40375                         
40376             m.render();
40377             // it's 10 on top level, and 1 on others??? why...
40378             return progressRun.defer(10, _this);
40379              
40380         }
40381         progressRun.defer(1, _this);
40382      
40383         
40384         
40385     },
40386         
40387         
40388         /**
40389          * Event Object.
40390          *
40391          *
40392          */
40393         event: false, 
40394     /**
40395          * wrapper for event.on - aliased later..  
40396          * Typically use to register a event handler for register:
40397          *
40398          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
40399          *
40400          */
40401     on : false
40402    
40403     
40404     
40405 });
40406
40407 Roo.XComponent.event = new Roo.util.Observable({
40408                 events : { 
40409                         /**
40410                          * @event register
40411                          * Fires when an Component is registered,
40412                          * set the disable property on the Component to stop registration.
40413                          * @param {Roo.XComponent} c the component being registerd.
40414                          * 
40415                          */
40416                         'register' : true,
40417             /**
40418                          * @event beforebuild
40419                          * Fires before each Component is built
40420                          * can be used to apply permissions.
40421                          * @param {Roo.XComponent} c the component being registerd.
40422                          * 
40423                          */
40424                         'beforebuild' : true,
40425                         /**
40426                          * @event buildcomplete
40427                          * Fires on the top level element when all elements have been built
40428                          * @param {Roo.XComponent} the top level component.
40429                          */
40430                         'buildcomplete' : true
40431                         
40432                 }
40433 });
40434
40435 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
40436