roojs-ui-debug.js
[roojs1] / roojs-ui-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12
13
14 /*
15  * These classes are derivatives of the similarly named classes in the YUI Library.
16  * The original license:
17  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
18  * Code licensed under the BSD License:
19  * http://developer.yahoo.net/yui/license.txt
20  */
21
22 (function() {
23
24 var Event=Roo.EventManager;
25 var Dom=Roo.lib.Dom;
26
27 /**
28  * @class Roo.dd.DragDrop
29  * @extends Roo.util.Observable
30  * Defines the interface and base operation of items that that can be
31  * dragged or can be drop targets.  It was designed to be extended, overriding
32  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
33  * Up to three html elements can be associated with a DragDrop instance:
34  * <ul>
35  * <li>linked element: the element that is passed into the constructor.
36  * This is the element which defines the boundaries for interaction with
37  * other DragDrop objects.</li>
38  * <li>handle element(s): The drag operation only occurs if the element that
39  * was clicked matches a handle element.  By default this is the linked
40  * element, but there are times that you will want only a portion of the
41  * linked element to initiate the drag operation, and the setHandleElId()
42  * method provides a way to define this.</li>
43  * <li>drag element: this represents the element that would be moved along
44  * with the cursor during a drag operation.  By default, this is the linked
45  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
46  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
47  * </li>
48  * </ul>
49  * This class should not be instantiated until the onload event to ensure that
50  * the associated elements are available.
51  * The following would define a DragDrop obj that would interact with any
52  * other DragDrop obj in the "group1" group:
53  * <pre>
54  *  dd = new Roo.dd.DragDrop("div1", "group1");
55  * </pre>
56  * Since none of the event handlers have been implemented, nothing would
57  * actually happen if you were to run the code above.  Normally you would
58  * override this class or one of the default implementations, but you can
59  * also override the methods you want on an instance of the class...
60  * <pre>
61  *  dd.onDragDrop = function(e, id) {
62  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
63  *  }
64  * </pre>
65  * @constructor
66  * @param {String} id of the element that is linked to this instance
67  * @param {String} sGroup the group of related DragDrop objects
68  * @param {object} config an object containing configurable attributes
69  *                Valid properties for DragDrop:
70  *                    padding, isTarget, maintainOffset, primaryButtonOnly
71  */
72 Roo.dd.DragDrop = function(id, sGroup, config) {
73     if (id) {
74         this.init(id, sGroup, config);
75     }
76     
77 };
78
79 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
80
81     /**
82      * The id of the element associated with this object.  This is what we
83      * refer to as the "linked element" because the size and position of
84      * this element is used to determine when the drag and drop objects have
85      * interacted.
86      * @property id
87      * @type String
88      */
89     id: null,
90
91     /**
92      * Configuration attributes passed into the constructor
93      * @property config
94      * @type object
95      */
96     config: null,
97
98     /**
99      * The id of the element that will be dragged.  By default this is same
100      * as the linked element , but could be changed to another element. Ex:
101      * Roo.dd.DDProxy
102      * @property dragElId
103      * @type String
104      * @private
105      */
106     dragElId: null,
107
108     /**
109      * the id of the element that initiates the drag operation.  By default
110      * this is the linked element, but could be changed to be a child of this
111      * element.  This lets us do things like only starting the drag when the
112      * header element within the linked html element is clicked.
113      * @property handleElId
114      * @type String
115      * @private
116      */
117     handleElId: null,
118
119     /**
120      * An associative array of HTML tags that will be ignored if clicked.
121      * @property invalidHandleTypes
122      * @type {string: string}
123      */
124     invalidHandleTypes: null,
125
126     /**
127      * An associative array of ids for elements that will be ignored if clicked
128      * @property invalidHandleIds
129      * @type {string: string}
130      */
131     invalidHandleIds: null,
132
133     /**
134      * An indexted array of css class names for elements that will be ignored
135      * if clicked.
136      * @property invalidHandleClasses
137      * @type string[]
138      */
139     invalidHandleClasses: null,
140
141     /**
142      * The linked element's absolute X position at the time the drag was
143      * started
144      * @property startPageX
145      * @type int
146      * @private
147      */
148     startPageX: 0,
149
150     /**
151      * The linked element's absolute X position at the time the drag was
152      * started
153      * @property startPageY
154      * @type int
155      * @private
156      */
157     startPageY: 0,
158
159     /**
160      * The group defines a logical collection of DragDrop objects that are
161      * related.  Instances only get events when interacting with other
162      * DragDrop object in the same group.  This lets us define multiple
163      * groups using a single DragDrop subclass if we want.
164      * @property groups
165      * @type {string: string}
166      */
167     groups: null,
168
169     /**
170      * Individual drag/drop instances can be locked.  This will prevent
171      * onmousedown start drag.
172      * @property locked
173      * @type boolean
174      * @private
175      */
176     locked: false,
177
178     /**
179      * Lock this instance
180      * @method lock
181      */
182     lock: function() { this.locked = true; },
183
184     /**
185      * Unlock this instace
186      * @method unlock
187      */
188     unlock: function() { this.locked = false; },
189
190     /**
191      * By default, all insances can be a drop target.  This can be disabled by
192      * setting isTarget to false.
193      * @method isTarget
194      * @type boolean
195      */
196     isTarget: true,
197
198     /**
199      * The padding configured for this drag and drop object for calculating
200      * the drop zone intersection with this object.
201      * @method padding
202      * @type int[]
203      */
204     padding: null,
205
206     /**
207      * Cached reference to the linked element
208      * @property _domRef
209      * @private
210      */
211     _domRef: null,
212
213     /**
214      * Internal typeof flag
215      * @property __ygDragDrop
216      * @private
217      */
218     __ygDragDrop: true,
219
220     /**
221      * Set to true when horizontal contraints are applied
222      * @property constrainX
223      * @type boolean
224      * @private
225      */
226     constrainX: false,
227
228     /**
229      * Set to true when vertical contraints are applied
230      * @property constrainY
231      * @type boolean
232      * @private
233      */
234     constrainY: false,
235
236     /**
237      * The left constraint
238      * @property minX
239      * @type int
240      * @private
241      */
242     minX: 0,
243
244     /**
245      * The right constraint
246      * @property maxX
247      * @type int
248      * @private
249      */
250     maxX: 0,
251
252     /**
253      * The up constraint
254      * @property minY
255      * @type int
256      * @type int
257      * @private
258      */
259     minY: 0,
260
261     /**
262      * The down constraint
263      * @property maxY
264      * @type int
265      * @private
266      */
267     maxY: 0,
268
269     /**
270      * Maintain offsets when we resetconstraints.  Set to true when you want
271      * the position of the element relative to its parent to stay the same
272      * when the page changes
273      *
274      * @property maintainOffset
275      * @type boolean
276      */
277     maintainOffset: false,
278
279     /**
280      * Array of pixel locations the element will snap to if we specified a
281      * horizontal graduation/interval.  This array is generated automatically
282      * when you define a tick interval.
283      * @property xTicks
284      * @type int[]
285      */
286     xTicks: null,
287
288     /**
289      * Array of pixel locations the element will snap to if we specified a
290      * vertical graduation/interval.  This array is generated automatically
291      * when you define a tick interval.
292      * @property yTicks
293      * @type int[]
294      */
295     yTicks: null,
296
297     /**
298      * By default the drag and drop instance will only respond to the primary
299      * button click (left button for a right-handed mouse).  Set to true to
300      * allow drag and drop to start with any mouse click that is propogated
301      * by the browser
302      * @property primaryButtonOnly
303      * @type boolean
304      */
305     primaryButtonOnly: true,
306
307     /**
308      * The availabe property is false until the linked dom element is accessible.
309      * @property available
310      * @type boolean
311      */
312     available: false,
313
314     /**
315      * By default, drags can only be initiated if the mousedown occurs in the
316      * region the linked element is.  This is done in part to work around a
317      * bug in some browsers that mis-report the mousedown if the previous
318      * mouseup happened outside of the window.  This property is set to true
319      * if outer handles are defined.
320      *
321      * @property hasOuterHandles
322      * @type boolean
323      * @default false
324      */
325     hasOuterHandles: false,
326
327     /**
328      * Code that executes immediately before the startDrag event
329      * @method b4StartDrag
330      * @private
331      */
332     b4StartDrag: function(x, y) { },
333
334     /**
335      * Abstract method called after a drag/drop object is clicked
336      * and the drag or mousedown time thresholds have beeen met.
337      * @method startDrag
338      * @param {int} X click location
339      * @param {int} Y click location
340      */
341     startDrag: function(x, y) { /* override this */ },
342
343     /**
344      * Code that executes immediately before the onDrag event
345      * @method b4Drag
346      * @private
347      */
348     b4Drag: function(e) { },
349
350     /**
351      * Abstract method called during the onMouseMove event while dragging an
352      * object.
353      * @method onDrag
354      * @param {Event} e the mousemove event
355      */
356     onDrag: function(e) { /* override this */ },
357
358     /**
359      * Abstract method called when this element fist begins hovering over
360      * another DragDrop obj
361      * @method onDragEnter
362      * @param {Event} e the mousemove event
363      * @param {String|DragDrop[]} id In POINT mode, the element
364      * id this is hovering over.  In INTERSECT mode, an array of one or more
365      * dragdrop items being hovered over.
366      */
367     onDragEnter: function(e, id) { /* override this */ },
368
369     /**
370      * Code that executes immediately before the onDragOver event
371      * @method b4DragOver
372      * @private
373      */
374     b4DragOver: function(e) { },
375
376     /**
377      * Abstract method called when this element is hovering over another
378      * DragDrop obj
379      * @method onDragOver
380      * @param {Event} e the mousemove event
381      * @param {String|DragDrop[]} id In POINT mode, the element
382      * id this is hovering over.  In INTERSECT mode, an array of dd items
383      * being hovered over.
384      */
385     onDragOver: function(e, id) { /* override this */ },
386
387     /**
388      * Code that executes immediately before the onDragOut event
389      * @method b4DragOut
390      * @private
391      */
392     b4DragOut: function(e) { },
393
394     /**
395      * Abstract method called when we are no longer hovering over an element
396      * @method onDragOut
397      * @param {Event} e the mousemove event
398      * @param {String|DragDrop[]} id In POINT mode, the element
399      * id this was hovering over.  In INTERSECT mode, an array of dd items
400      * that the mouse is no longer over.
401      */
402     onDragOut: function(e, id) { /* override this */ },
403
404     /**
405      * Code that executes immediately before the onDragDrop event
406      * @method b4DragDrop
407      * @private
408      */
409     b4DragDrop: function(e) { },
410
411     /**
412      * Abstract method called when this item is dropped on another DragDrop
413      * obj
414      * @method onDragDrop
415      * @param {Event} e the mouseup event
416      * @param {String|DragDrop[]} id In POINT mode, the element
417      * id this was dropped on.  In INTERSECT mode, an array of dd items this
418      * was dropped on.
419      */
420     onDragDrop: function(e, id) { /* override this */ },
421
422     /**
423      * Abstract method called when this item is dropped on an area with no
424      * drop target
425      * @method onInvalidDrop
426      * @param {Event} e the mouseup event
427      */
428     onInvalidDrop: function(e) { /* override this */ },
429
430     /**
431      * Code that executes immediately before the endDrag event
432      * @method b4EndDrag
433      * @private
434      */
435     b4EndDrag: function(e) { },
436
437     /**
438      * Fired when we are done dragging the object
439      * @method endDrag
440      * @param {Event} e the mouseup event
441      */
442     endDrag: function(e) { /* override this */ },
443
444     /**
445      * Code executed immediately before the onMouseDown event
446      * @method b4MouseDown
447      * @param {Event} e the mousedown event
448      * @private
449      */
450     b4MouseDown: function(e) {  },
451
452     /**
453      * Event handler that fires when a drag/drop obj gets a mousedown
454      * @method onMouseDown
455      * @param {Event} e the mousedown event
456      */
457     onMouseDown: function(e) { /* override this */ },
458
459     /**
460      * Event handler that fires when a drag/drop obj gets a mouseup
461      * @method onMouseUp
462      * @param {Event} e the mouseup event
463      */
464     onMouseUp: function(e) { /* override this */ },
465
466     /**
467      * Override the onAvailable method to do what is needed after the initial
468      * position was determined.
469      * @method onAvailable
470      */
471     onAvailable: function () {
472     },
473
474     /*
475      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
476      * @type Object
477      */
478     defaultPadding : {left:0, right:0, top:0, bottom:0},
479
480     /*
481      * Initializes the drag drop object's constraints to restrict movement to a certain element.
482  *
483  * Usage:
484  <pre><code>
485  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
486                 { dragElId: "existingProxyDiv" });
487  dd.startDrag = function(){
488      this.constrainTo("parent-id");
489  };
490  </code></pre>
491  * Or you can initalize it using the {@link Roo.Element} object:
492  <pre><code>
493  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
494      startDrag : function(){
495          this.constrainTo("parent-id");
496      }
497  });
498  </code></pre>
499      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
500      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
501      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
502      * an object containing the sides to pad. For example: {right:10, bottom:10}
503      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
504      */
505     constrainTo : function(constrainTo, pad, inContent){
506         if(typeof pad == "number"){
507             pad = {left: pad, right:pad, top:pad, bottom:pad};
508         }
509         pad = pad || this.defaultPadding;
510         var b = Roo.get(this.getEl()).getBox();
511         var ce = Roo.get(constrainTo);
512         var s = ce.getScroll();
513         var c, cd = ce.dom;
514         if(cd == document.body){
515             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
516         }else{
517             xy = ce.getXY();
518             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
519         }
520
521
522         var topSpace = b.y - c.y;
523         var leftSpace = b.x - c.x;
524
525         this.resetConstraints();
526         this.setXConstraint(leftSpace - (pad.left||0), // left
527                 c.width - leftSpace - b.width - (pad.right||0) //right
528         );
529         this.setYConstraint(topSpace - (pad.top||0), //top
530                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
531         );
532     },
533
534     /**
535      * Returns a reference to the linked element
536      * @method getEl
537      * @return {HTMLElement} the html element
538      */
539     getEl: function() {
540         if (!this._domRef) {
541             this._domRef = Roo.getDom(this.id);
542         }
543
544         return this._domRef;
545     },
546
547     /**
548      * Returns a reference to the actual element to drag.  By default this is
549      * the same as the html element, but it can be assigned to another
550      * element. An example of this can be found in Roo.dd.DDProxy
551      * @method getDragEl
552      * @return {HTMLElement} the html element
553      */
554     getDragEl: function() {
555         return Roo.getDom(this.dragElId);
556     },
557
558     /**
559      * Sets up the DragDrop object.  Must be called in the constructor of any
560      * Roo.dd.DragDrop subclass
561      * @method init
562      * @param id the id of the linked element
563      * @param {String} sGroup the group of related items
564      * @param {object} config configuration attributes
565      */
566     init: function(id, sGroup, config) {
567         this.initTarget(id, sGroup, config);
568         Event.on(this.id, "mousedown", this.handleMouseDown, this);
569         // Event.on(this.id, "selectstart", Event.preventDefault);
570     },
571
572     /**
573      * Initializes Targeting functionality only... the object does not
574      * get a mousedown handler.
575      * @method initTarget
576      * @param id the id of the linked element
577      * @param {String} sGroup the group of related items
578      * @param {object} config configuration attributes
579      */
580     initTarget: function(id, sGroup, config) {
581
582         // configuration attributes
583         this.config = config || {};
584
585         // create a local reference to the drag and drop manager
586         this.DDM = Roo.dd.DDM;
587         // initialize the groups array
588         this.groups = {};
589
590         // assume that we have an element reference instead of an id if the
591         // parameter is not a string
592         if (typeof id !== "string") {
593             id = Roo.id(id);
594         }
595
596         // set the id
597         this.id = id;
598
599         // add to an interaction group
600         this.addToGroup((sGroup) ? sGroup : "default");
601
602         // We don't want to register this as the handle with the manager
603         // so we just set the id rather than calling the setter.
604         this.handleElId = id;
605
606         // the linked element is the element that gets dragged by default
607         this.setDragElId(id);
608
609         // by default, clicked anchors will not start drag operations.
610         this.invalidHandleTypes = { A: "A" };
611         this.invalidHandleIds = {};
612         this.invalidHandleClasses = [];
613
614         this.applyConfig();
615
616         this.handleOnAvailable();
617     },
618
619     /**
620      * Applies the configuration parameters that were passed into the constructor.
621      * This is supposed to happen at each level through the inheritance chain.  So
622      * a DDProxy implentation will execute apply config on DDProxy, DD, and
623      * DragDrop in order to get all of the parameters that are available in
624      * each object.
625      * @method applyConfig
626      */
627     applyConfig: function() {
628
629         // configurable properties:
630         //    padding, isTarget, maintainOffset, primaryButtonOnly
631         this.padding           = this.config.padding || [0, 0, 0, 0];
632         this.isTarget          = (this.config.isTarget !== false);
633         this.maintainOffset    = (this.config.maintainOffset);
634         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
635
636     },
637
638     /**
639      * Executed when the linked element is available
640      * @method handleOnAvailable
641      * @private
642      */
643     handleOnAvailable: function() {
644         this.available = true;
645         this.resetConstraints();
646         this.onAvailable();
647     },
648
649      /**
650      * Configures the padding for the target zone in px.  Effectively expands
651      * (or reduces) the virtual object size for targeting calculations.
652      * Supports css-style shorthand; if only one parameter is passed, all sides
653      * will have that padding, and if only two are passed, the top and bottom
654      * will have the first param, the left and right the second.
655      * @method setPadding
656      * @param {int} iTop    Top pad
657      * @param {int} iRight  Right pad
658      * @param {int} iBot    Bot pad
659      * @param {int} iLeft   Left pad
660      */
661     setPadding: function(iTop, iRight, iBot, iLeft) {
662         // this.padding = [iLeft, iRight, iTop, iBot];
663         if (!iRight && 0 !== iRight) {
664             this.padding = [iTop, iTop, iTop, iTop];
665         } else if (!iBot && 0 !== iBot) {
666             this.padding = [iTop, iRight, iTop, iRight];
667         } else {
668             this.padding = [iTop, iRight, iBot, iLeft];
669         }
670     },
671
672     /**
673      * Stores the initial placement of the linked element.
674      * @method setInitialPosition
675      * @param {int} diffX   the X offset, default 0
676      * @param {int} diffY   the Y offset, default 0
677      */
678     setInitPosition: function(diffX, diffY) {
679         var el = this.getEl();
680
681         if (!this.DDM.verifyEl(el)) {
682             return;
683         }
684
685         var dx = diffX || 0;
686         var dy = diffY || 0;
687
688         var p = Dom.getXY( el );
689
690         this.initPageX = p[0] - dx;
691         this.initPageY = p[1] - dy;
692
693         this.lastPageX = p[0];
694         this.lastPageY = p[1];
695
696
697         this.setStartPosition(p);
698     },
699
700     /**
701      * Sets the start position of the element.  This is set when the obj
702      * is initialized, the reset when a drag is started.
703      * @method setStartPosition
704      * @param pos current position (from previous lookup)
705      * @private
706      */
707     setStartPosition: function(pos) {
708         var p = pos || Dom.getXY( this.getEl() );
709         this.deltaSetXY = null;
710
711         this.startPageX = p[0];
712         this.startPageY = p[1];
713     },
714
715     /**
716      * Add this instance to a group of related drag/drop objects.  All
717      * instances belong to at least one group, and can belong to as many
718      * groups as needed.
719      * @method addToGroup
720      * @param sGroup {string} the name of the group
721      */
722     addToGroup: function(sGroup) {
723         this.groups[sGroup] = true;
724         this.DDM.regDragDrop(this, sGroup);
725     },
726
727     /**
728      * Remove's this instance from the supplied interaction group
729      * @method removeFromGroup
730      * @param {string}  sGroup  The group to drop
731      */
732     removeFromGroup: function(sGroup) {
733         if (this.groups[sGroup]) {
734             delete this.groups[sGroup];
735         }
736
737         this.DDM.removeDDFromGroup(this, sGroup);
738     },
739
740     /**
741      * Allows you to specify that an element other than the linked element
742      * will be moved with the cursor during a drag
743      * @method setDragElId
744      * @param id {string} the id of the element that will be used to initiate the drag
745      */
746     setDragElId: function(id) {
747         this.dragElId = id;
748     },
749
750     /**
751      * Allows you to specify a child of the linked element that should be
752      * used to initiate the drag operation.  An example of this would be if
753      * you have a content div with text and links.  Clicking anywhere in the
754      * content area would normally start the drag operation.  Use this method
755      * to specify that an element inside of the content div is the element
756      * that starts the drag operation.
757      * @method setHandleElId
758      * @param id {string} the id of the element that will be used to
759      * initiate the drag.
760      */
761     setHandleElId: function(id) {
762         if (typeof id !== "string") {
763             id = Roo.id(id);
764         }
765         this.handleElId = id;
766         this.DDM.regHandle(this.id, id);
767     },
768
769     /**
770      * Allows you to set an element outside of the linked element as a drag
771      * handle
772      * @method setOuterHandleElId
773      * @param id the id of the element that will be used to initiate the drag
774      */
775     setOuterHandleElId: function(id) {
776         if (typeof id !== "string") {
777             id = Roo.id(id);
778         }
779         Event.on(id, "mousedown",
780                 this.handleMouseDown, this);
781         this.setHandleElId(id);
782
783         this.hasOuterHandles = true;
784     },
785
786     /**
787      * Remove all drag and drop hooks for this element
788      * @method unreg
789      */
790     unreg: function() {
791         Event.un(this.id, "mousedown",
792                 this.handleMouseDown);
793         this._domRef = null;
794         this.DDM._remove(this);
795     },
796
797     destroy : function(){
798         this.unreg();
799     },
800
801     /**
802      * Returns true if this instance is locked, or the drag drop mgr is locked
803      * (meaning that all drag/drop is disabled on the page.)
804      * @method isLocked
805      * @return {boolean} true if this obj or all drag/drop is locked, else
806      * false
807      */
808     isLocked: function() {
809         return (this.DDM.isLocked() || this.locked);
810     },
811
812     /**
813      * Fired when this object is clicked
814      * @method handleMouseDown
815      * @param {Event} e
816      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
817      * @private
818      */
819     handleMouseDown: function(e, oDD){
820         if (this.primaryButtonOnly && e.button != 0) {
821             return;
822         }
823
824         if (this.isLocked()) {
825             return;
826         }
827
828         this.DDM.refreshCache(this.groups);
829
830         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
831         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
832         } else {
833             if (this.clickValidator(e)) {
834
835                 // set the initial element position
836                 this.setStartPosition();
837
838
839                 this.b4MouseDown(e);
840                 this.onMouseDown(e);
841
842                 this.DDM.handleMouseDown(e, this);
843
844                 this.DDM.stopEvent(e);
845             } else {
846
847
848             }
849         }
850     },
851
852     clickValidator: function(e) {
853         var target = e.getTarget();
854         return ( this.isValidHandleChild(target) &&
855                     (this.id == this.handleElId ||
856                         this.DDM.handleWasClicked(target, this.id)) );
857     },
858
859     /**
860      * Allows you to specify a tag name that should not start a drag operation
861      * when clicked.  This is designed to facilitate embedding links within a
862      * drag handle that do something other than start the drag.
863      * @method addInvalidHandleType
864      * @param {string} tagName the type of element to exclude
865      */
866     addInvalidHandleType: function(tagName) {
867         var type = tagName.toUpperCase();
868         this.invalidHandleTypes[type] = type;
869     },
870
871     /**
872      * Lets you to specify an element id for a child of a drag handle
873      * that should not initiate a drag
874      * @method addInvalidHandleId
875      * @param {string} id the element id of the element you wish to ignore
876      */
877     addInvalidHandleId: function(id) {
878         if (typeof id !== "string") {
879             id = Roo.id(id);
880         }
881         this.invalidHandleIds[id] = id;
882     },
883
884     /**
885      * Lets you specify a css class of elements that will not initiate a drag
886      * @method addInvalidHandleClass
887      * @param {string} cssClass the class of the elements you wish to ignore
888      */
889     addInvalidHandleClass: function(cssClass) {
890         this.invalidHandleClasses.push(cssClass);
891     },
892
893     /**
894      * Unsets an excluded tag name set by addInvalidHandleType
895      * @method removeInvalidHandleType
896      * @param {string} tagName the type of element to unexclude
897      */
898     removeInvalidHandleType: function(tagName) {
899         var type = tagName.toUpperCase();
900         // this.invalidHandleTypes[type] = null;
901         delete this.invalidHandleTypes[type];
902     },
903
904     /**
905      * Unsets an invalid handle id
906      * @method removeInvalidHandleId
907      * @param {string} id the id of the element to re-enable
908      */
909     removeInvalidHandleId: function(id) {
910         if (typeof id !== "string") {
911             id = Roo.id(id);
912         }
913         delete this.invalidHandleIds[id];
914     },
915
916     /**
917      * Unsets an invalid css class
918      * @method removeInvalidHandleClass
919      * @param {string} cssClass the class of the element(s) you wish to
920      * re-enable
921      */
922     removeInvalidHandleClass: function(cssClass) {
923         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
924             if (this.invalidHandleClasses[i] == cssClass) {
925                 delete this.invalidHandleClasses[i];
926             }
927         }
928     },
929
930     /**
931      * Checks the tag exclusion list to see if this click should be ignored
932      * @method isValidHandleChild
933      * @param {HTMLElement} node the HTMLElement to evaluate
934      * @return {boolean} true if this is a valid tag type, false if not
935      */
936     isValidHandleChild: function(node) {
937
938         var valid = true;
939         // var n = (node.nodeName == "#text") ? node.parentNode : node;
940         var nodeName;
941         try {
942             nodeName = node.nodeName.toUpperCase();
943         } catch(e) {
944             nodeName = node.nodeName;
945         }
946         valid = valid && !this.invalidHandleTypes[nodeName];
947         valid = valid && !this.invalidHandleIds[node.id];
948
949         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
950             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
951         }
952
953
954         return valid;
955
956     },
957
958     /**
959      * Create the array of horizontal tick marks if an interval was specified
960      * in setXConstraint().
961      * @method setXTicks
962      * @private
963      */
964     setXTicks: function(iStartX, iTickSize) {
965         this.xTicks = [];
966         this.xTickSize = iTickSize;
967
968         var tickMap = {};
969
970         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
971             if (!tickMap[i]) {
972                 this.xTicks[this.xTicks.length] = i;
973                 tickMap[i] = true;
974             }
975         }
976
977         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
978             if (!tickMap[i]) {
979                 this.xTicks[this.xTicks.length] = i;
980                 tickMap[i] = true;
981             }
982         }
983
984         this.xTicks.sort(this.DDM.numericSort) ;
985     },
986
987     /**
988      * Create the array of vertical tick marks if an interval was specified in
989      * setYConstraint().
990      * @method setYTicks
991      * @private
992      */
993     setYTicks: function(iStartY, iTickSize) {
994         this.yTicks = [];
995         this.yTickSize = iTickSize;
996
997         var tickMap = {};
998
999         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
1000             if (!tickMap[i]) {
1001                 this.yTicks[this.yTicks.length] = i;
1002                 tickMap[i] = true;
1003             }
1004         }
1005
1006         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
1007             if (!tickMap[i]) {
1008                 this.yTicks[this.yTicks.length] = i;
1009                 tickMap[i] = true;
1010             }
1011         }
1012
1013         this.yTicks.sort(this.DDM.numericSort) ;
1014     },
1015
1016     /**
1017      * By default, the element can be dragged any place on the screen.  Use
1018      * this method to limit the horizontal travel of the element.  Pass in
1019      * 0,0 for the parameters if you want to lock the drag to the y axis.
1020      * @method setXConstraint
1021      * @param {int} iLeft the number of pixels the element can move to the left
1022      * @param {int} iRight the number of pixels the element can move to the
1023      * right
1024      * @param {int} iTickSize optional parameter for specifying that the
1025      * element
1026      * should move iTickSize pixels at a time.
1027      */
1028     setXConstraint: function(iLeft, iRight, iTickSize) {
1029         this.leftConstraint = iLeft;
1030         this.rightConstraint = iRight;
1031
1032         this.minX = this.initPageX - iLeft;
1033         this.maxX = this.initPageX + iRight;
1034         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
1035
1036         this.constrainX = true;
1037     },
1038
1039     /**
1040      * Clears any constraints applied to this instance.  Also clears ticks
1041      * since they can't exist independent of a constraint at this time.
1042      * @method clearConstraints
1043      */
1044     clearConstraints: function() {
1045         this.constrainX = false;
1046         this.constrainY = false;
1047         this.clearTicks();
1048     },
1049
1050     /**
1051      * Clears any tick interval defined for this instance
1052      * @method clearTicks
1053      */
1054     clearTicks: function() {
1055         this.xTicks = null;
1056         this.yTicks = null;
1057         this.xTickSize = 0;
1058         this.yTickSize = 0;
1059     },
1060
1061     /**
1062      * By default, the element can be dragged any place on the screen.  Set
1063      * this to limit the vertical travel of the element.  Pass in 0,0 for the
1064      * parameters if you want to lock the drag to the x axis.
1065      * @method setYConstraint
1066      * @param {int} iUp the number of pixels the element can move up
1067      * @param {int} iDown the number of pixels the element can move down
1068      * @param {int} iTickSize optional parameter for specifying that the
1069      * element should move iTickSize pixels at a time.
1070      */
1071     setYConstraint: function(iUp, iDown, iTickSize) {
1072         this.topConstraint = iUp;
1073         this.bottomConstraint = iDown;
1074
1075         this.minY = this.initPageY - iUp;
1076         this.maxY = this.initPageY + iDown;
1077         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
1078
1079         this.constrainY = true;
1080
1081     },
1082
1083     /**
1084      * resetConstraints must be called if you manually reposition a dd element.
1085      * @method resetConstraints
1086      * @param {boolean} maintainOffset
1087      */
1088     resetConstraints: function() {
1089
1090
1091         // Maintain offsets if necessary
1092         if (this.initPageX || this.initPageX === 0) {
1093             // figure out how much this thing has moved
1094             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
1095             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
1096
1097             this.setInitPosition(dx, dy);
1098
1099         // This is the first time we have detected the element's position
1100         } else {
1101             this.setInitPosition();
1102         }
1103
1104         if (this.constrainX) {
1105             this.setXConstraint( this.leftConstraint,
1106                                  this.rightConstraint,
1107                                  this.xTickSize        );
1108         }
1109
1110         if (this.constrainY) {
1111             this.setYConstraint( this.topConstraint,
1112                                  this.bottomConstraint,
1113                                  this.yTickSize         );
1114         }
1115     },
1116
1117     /**
1118      * Normally the drag element is moved pixel by pixel, but we can specify
1119      * that it move a number of pixels at a time.  This method resolves the
1120      * location when we have it set up like this.
1121      * @method getTick
1122      * @param {int} val where we want to place the object
1123      * @param {int[]} tickArray sorted array of valid points
1124      * @return {int} the closest tick
1125      * @private
1126      */
1127     getTick: function(val, tickArray) {
1128
1129         if (!tickArray) {
1130             // If tick interval is not defined, it is effectively 1 pixel,
1131             // so we return the value passed to us.
1132             return val;
1133         } else if (tickArray[0] >= val) {
1134             // The value is lower than the first tick, so we return the first
1135             // tick.
1136             return tickArray[0];
1137         } else {
1138             for (var i=0, len=tickArray.length; i<len; ++i) {
1139                 var next = i + 1;
1140                 if (tickArray[next] && tickArray[next] >= val) {
1141                     var diff1 = val - tickArray[i];
1142                     var diff2 = tickArray[next] - val;
1143                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
1144                 }
1145             }
1146
1147             // The value is larger than the last tick, so we return the last
1148             // tick.
1149             return tickArray[tickArray.length - 1];
1150         }
1151     },
1152
1153     /**
1154      * toString method
1155      * @method toString
1156      * @return {string} string representation of the dd obj
1157      */
1158     toString: function() {
1159         return ("DragDrop " + this.id);
1160     }
1161
1162 });
1163
1164 })();
1165 /*
1166  * Based on:
1167  * Ext JS Library 1.1.1
1168  * Copyright(c) 2006-2007, Ext JS, LLC.
1169  *
1170  * Originally Released Under LGPL - original licence link has changed is not relivant.
1171  *
1172  * Fork - LGPL
1173  * <script type="text/javascript">
1174  */
1175
1176
1177 /**
1178  * The drag and drop utility provides a framework for building drag and drop
1179  * applications.  In addition to enabling drag and drop for specific elements,
1180  * the drag and drop elements are tracked by the manager class, and the
1181  * interactions between the various elements are tracked during the drag and
1182  * the implementing code is notified about these important moments.
1183  */
1184
1185 // Only load the library once.  Rewriting the manager class would orphan
1186 // existing drag and drop instances.
1187 if (!Roo.dd.DragDropMgr) {
1188
1189 /**
1190  * @class Roo.dd.DragDropMgr
1191  * DragDropMgr is a singleton that tracks the element interaction for
1192  * all DragDrop items in the window.  Generally, you will not call
1193  * this class directly, but it does have helper methods that could
1194  * be useful in your DragDrop implementations.
1195  * @singleton
1196  */
1197 Roo.dd.DragDropMgr = function() {
1198
1199     var Event = Roo.EventManager;
1200
1201     return {
1202
1203         /**
1204          * Two dimensional Array of registered DragDrop objects.  The first
1205          * dimension is the DragDrop item group, the second the DragDrop
1206          * object.
1207          * @property ids
1208          * @type {string: string}
1209          * @private
1210          * @static
1211          */
1212         ids: {},
1213
1214         /**
1215          * Array of element ids defined as drag handles.  Used to determine
1216          * if the element that generated the mousedown event is actually the
1217          * handle and not the html element itself.
1218          * @property handleIds
1219          * @type {string: string}
1220          * @private
1221          * @static
1222          */
1223         handleIds: {},
1224
1225         /**
1226          * the DragDrop object that is currently being dragged
1227          * @property dragCurrent
1228          * @type DragDrop
1229          * @private
1230          * @static
1231          **/
1232         dragCurrent: null,
1233
1234         /**
1235          * the DragDrop object(s) that are being hovered over
1236          * @property dragOvers
1237          * @type Array
1238          * @private
1239          * @static
1240          */
1241         dragOvers: {},
1242
1243         /**
1244          * the X distance between the cursor and the object being dragged
1245          * @property deltaX
1246          * @type int
1247          * @private
1248          * @static
1249          */
1250         deltaX: 0,
1251
1252         /**
1253          * the Y distance between the cursor and the object being dragged
1254          * @property deltaY
1255          * @type int
1256          * @private
1257          * @static
1258          */
1259         deltaY: 0,
1260
1261         /**
1262          * Flag to determine if we should prevent the default behavior of the
1263          * events we define. By default this is true, but this can be set to
1264          * false if you need the default behavior (not recommended)
1265          * @property preventDefault
1266          * @type boolean
1267          * @static
1268          */
1269         preventDefault: true,
1270
1271         /**
1272          * Flag to determine if we should stop the propagation of the events
1273          * we generate. This is true by default but you may want to set it to
1274          * false if the html element contains other features that require the
1275          * mouse click.
1276          * @property stopPropagation
1277          * @type boolean
1278          * @static
1279          */
1280         stopPropagation: true,
1281
1282         /**
1283          * Internal flag that is set to true when drag and drop has been
1284          * intialized
1285          * @property initialized
1286          * @private
1287          * @static
1288          */
1289         initalized: false,
1290
1291         /**
1292          * All drag and drop can be disabled.
1293          * @property locked
1294          * @private
1295          * @static
1296          */
1297         locked: false,
1298
1299         /**
1300          * Called the first time an element is registered.
1301          * @method init
1302          * @private
1303          * @static
1304          */
1305         init: function() {
1306             this.initialized = true;
1307         },
1308
1309         /**
1310          * In point mode, drag and drop interaction is defined by the
1311          * location of the cursor during the drag/drop
1312          * @property POINT
1313          * @type int
1314          * @static
1315          */
1316         POINT: 0,
1317
1318         /**
1319          * In intersect mode, drag and drop interactio nis defined by the
1320          * overlap of two or more drag and drop objects.
1321          * @property INTERSECT
1322          * @type int
1323          * @static
1324          */
1325         INTERSECT: 1,
1326
1327         /**
1328          * The current drag and drop mode.  Default: POINT
1329          * @property mode
1330          * @type int
1331          * @static
1332          */
1333         mode: 0,
1334
1335         /**
1336          * Runs method on all drag and drop objects
1337          * @method _execOnAll
1338          * @private
1339          * @static
1340          */
1341         _execOnAll: function(sMethod, args) {
1342             for (var i in this.ids) {
1343                 for (var j in this.ids[i]) {
1344                     var oDD = this.ids[i][j];
1345                     if (! this.isTypeOfDD(oDD)) {
1346                         continue;
1347                     }
1348                     oDD[sMethod].apply(oDD, args);
1349                 }
1350             }
1351         },
1352
1353         /**
1354          * Drag and drop initialization.  Sets up the global event handlers
1355          * @method _onLoad
1356          * @private
1357          * @static
1358          */
1359         _onLoad: function() {
1360
1361             this.init();
1362
1363
1364             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
1365             Event.on(document, "mousemove", this.handleMouseMove, this, true);
1366             Event.on(window,   "unload",    this._onUnload, this, true);
1367             Event.on(window,   "resize",    this._onResize, this, true);
1368             // Event.on(window,   "mouseout",    this._test);
1369
1370         },
1371
1372         /**
1373          * Reset constraints on all drag and drop objs
1374          * @method _onResize
1375          * @private
1376          * @static
1377          */
1378         _onResize: function(e) {
1379             this._execOnAll("resetConstraints", []);
1380         },
1381
1382         /**
1383          * Lock all drag and drop functionality
1384          * @method lock
1385          * @static
1386          */
1387         lock: function() { this.locked = true; },
1388
1389         /**
1390          * Unlock all drag and drop functionality
1391          * @method unlock
1392          * @static
1393          */
1394         unlock: function() { this.locked = false; },
1395
1396         /**
1397          * Is drag and drop locked?
1398          * @method isLocked
1399          * @return {boolean} True if drag and drop is locked, false otherwise.
1400          * @static
1401          */
1402         isLocked: function() { return this.locked; },
1403
1404         /**
1405          * Location cache that is set for all drag drop objects when a drag is
1406          * initiated, cleared when the drag is finished.
1407          * @property locationCache
1408          * @private
1409          * @static
1410          */
1411         locationCache: {},
1412
1413         /**
1414          * Set useCache to false if you want to force object the lookup of each
1415          * drag and drop linked element constantly during a drag.
1416          * @property useCache
1417          * @type boolean
1418          * @static
1419          */
1420         useCache: true,
1421
1422         /**
1423          * The number of pixels that the mouse needs to move after the
1424          * mousedown before the drag is initiated.  Default=3;
1425          * @property clickPixelThresh
1426          * @type int
1427          * @static
1428          */
1429         clickPixelThresh: 3,
1430
1431         /**
1432          * The number of milliseconds after the mousedown event to initiate the
1433          * drag if we don't get a mouseup event. Default=1000
1434          * @property clickTimeThresh
1435          * @type int
1436          * @static
1437          */
1438         clickTimeThresh: 350,
1439
1440         /**
1441          * Flag that indicates that either the drag pixel threshold or the
1442          * mousdown time threshold has been met
1443          * @property dragThreshMet
1444          * @type boolean
1445          * @private
1446          * @static
1447          */
1448         dragThreshMet: false,
1449
1450         /**
1451          * Timeout used for the click time threshold
1452          * @property clickTimeout
1453          * @type Object
1454          * @private
1455          * @static
1456          */
1457         clickTimeout: null,
1458
1459         /**
1460          * The X position of the mousedown event stored for later use when a
1461          * drag threshold is met.
1462          * @property startX
1463          * @type int
1464          * @private
1465          * @static
1466          */
1467         startX: 0,
1468
1469         /**
1470          * The Y position of the mousedown event stored for later use when a
1471          * drag threshold is met.
1472          * @property startY
1473          * @type int
1474          * @private
1475          * @static
1476          */
1477         startY: 0,
1478
1479         /**
1480          * Each DragDrop instance must be registered with the DragDropMgr.
1481          * This is executed in DragDrop.init()
1482          * @method regDragDrop
1483          * @param {DragDrop} oDD the DragDrop object to register
1484          * @param {String} sGroup the name of the group this element belongs to
1485          * @static
1486          */
1487         regDragDrop: function(oDD, sGroup) {
1488             if (!this.initialized) { this.init(); }
1489
1490             if (!this.ids[sGroup]) {
1491                 this.ids[sGroup] = {};
1492             }
1493             this.ids[sGroup][oDD.id] = oDD;
1494         },
1495
1496         /**
1497          * Removes the supplied dd instance from the supplied group. Executed
1498          * by DragDrop.removeFromGroup, so don't call this function directly.
1499          * @method removeDDFromGroup
1500          * @private
1501          * @static
1502          */
1503         removeDDFromGroup: function(oDD, sGroup) {
1504             if (!this.ids[sGroup]) {
1505                 this.ids[sGroup] = {};
1506             }
1507
1508             var obj = this.ids[sGroup];
1509             if (obj && obj[oDD.id]) {
1510                 delete obj[oDD.id];
1511             }
1512         },
1513
1514         /**
1515          * Unregisters a drag and drop item.  This is executed in
1516          * DragDrop.unreg, use that method instead of calling this directly.
1517          * @method _remove
1518          * @private
1519          * @static
1520          */
1521         _remove: function(oDD) {
1522             for (var g in oDD.groups) {
1523                 if (g && this.ids[g][oDD.id]) {
1524                     delete this.ids[g][oDD.id];
1525                 }
1526             }
1527             delete this.handleIds[oDD.id];
1528         },
1529
1530         /**
1531          * Each DragDrop handle element must be registered.  This is done
1532          * automatically when executing DragDrop.setHandleElId()
1533          * @method regHandle
1534          * @param {String} sDDId the DragDrop id this element is a handle for
1535          * @param {String} sHandleId the id of the element that is the drag
1536          * handle
1537          * @static
1538          */
1539         regHandle: function(sDDId, sHandleId) {
1540             if (!this.handleIds[sDDId]) {
1541                 this.handleIds[sDDId] = {};
1542             }
1543             this.handleIds[sDDId][sHandleId] = sHandleId;
1544         },
1545
1546         /**
1547          * Utility function to determine if a given element has been
1548          * registered as a drag drop item.
1549          * @method isDragDrop
1550          * @param {String} id the element id to check
1551          * @return {boolean} true if this element is a DragDrop item,
1552          * false otherwise
1553          * @static
1554          */
1555         isDragDrop: function(id) {
1556             return ( this.getDDById(id) ) ? true : false;
1557         },
1558
1559         /**
1560          * Returns the drag and drop instances that are in all groups the
1561          * passed in instance belongs to.
1562          * @method getRelated
1563          * @param {DragDrop} p_oDD the obj to get related data for
1564          * @param {boolean} bTargetsOnly if true, only return targetable objs
1565          * @return {DragDrop[]} the related instances
1566          * @static
1567          */
1568         getRelated: function(p_oDD, bTargetsOnly) {
1569             var oDDs = [];
1570             for (var i in p_oDD.groups) {
1571                 for (j in this.ids[i]) {
1572                     var dd = this.ids[i][j];
1573                     if (! this.isTypeOfDD(dd)) {
1574                         continue;
1575                     }
1576                     if (!bTargetsOnly || dd.isTarget) {
1577                         oDDs[oDDs.length] = dd;
1578                     }
1579                 }
1580             }
1581
1582             return oDDs;
1583         },
1584
1585         /**
1586          * Returns true if the specified dd target is a legal target for
1587          * the specifice drag obj
1588          * @method isLegalTarget
1589          * @param {DragDrop} the drag obj
1590          * @param {DragDrop} the target
1591          * @return {boolean} true if the target is a legal target for the
1592          * dd obj
1593          * @static
1594          */
1595         isLegalTarget: function (oDD, oTargetDD) {
1596             var targets = this.getRelated(oDD, true);
1597             for (var i=0, len=targets.length;i<len;++i) {
1598                 if (targets[i].id == oTargetDD.id) {
1599                     return true;
1600                 }
1601             }
1602
1603             return false;
1604         },
1605
1606         /**
1607          * My goal is to be able to transparently determine if an object is
1608          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
1609          * returns "object", oDD.constructor.toString() always returns
1610          * "DragDrop" and not the name of the subclass.  So for now it just
1611          * evaluates a well-known variable in DragDrop.
1612          * @method isTypeOfDD
1613          * @param {Object} the object to evaluate
1614          * @return {boolean} true if typeof oDD = DragDrop
1615          * @static
1616          */
1617         isTypeOfDD: function (oDD) {
1618             return (oDD && oDD.__ygDragDrop);
1619         },
1620
1621         /**
1622          * Utility function to determine if a given element has been
1623          * registered as a drag drop handle for the given Drag Drop object.
1624          * @method isHandle
1625          * @param {String} id the element id to check
1626          * @return {boolean} true if this element is a DragDrop handle, false
1627          * otherwise
1628          * @static
1629          */
1630         isHandle: function(sDDId, sHandleId) {
1631             return ( this.handleIds[sDDId] &&
1632                             this.handleIds[sDDId][sHandleId] );
1633         },
1634
1635         /**
1636          * Returns the DragDrop instance for a given id
1637          * @method getDDById
1638          * @param {String} id the id of the DragDrop object
1639          * @return {DragDrop} the drag drop object, null if it is not found
1640          * @static
1641          */
1642         getDDById: function(id) {
1643             for (var i in this.ids) {
1644                 if (this.ids[i][id]) {
1645                     return this.ids[i][id];
1646                 }
1647             }
1648             return null;
1649         },
1650
1651         /**
1652          * Fired after a registered DragDrop object gets the mousedown event.
1653          * Sets up the events required to track the object being dragged
1654          * @method handleMouseDown
1655          * @param {Event} e the event
1656          * @param oDD the DragDrop object being dragged
1657          * @private
1658          * @static
1659          */
1660         handleMouseDown: function(e, oDD) {
1661             if(Roo.QuickTips){
1662                 Roo.QuickTips.disable();
1663             }
1664             this.currentTarget = e.getTarget();
1665
1666             this.dragCurrent = oDD;
1667
1668             var el = oDD.getEl();
1669
1670             // track start position
1671             this.startX = e.getPageX();
1672             this.startY = e.getPageY();
1673
1674             this.deltaX = this.startX - el.offsetLeft;
1675             this.deltaY = this.startY - el.offsetTop;
1676
1677             this.dragThreshMet = false;
1678
1679             this.clickTimeout = setTimeout(
1680                     function() {
1681                         var DDM = Roo.dd.DDM;
1682                         DDM.startDrag(DDM.startX, DDM.startY);
1683                     },
1684                     this.clickTimeThresh );
1685         },
1686
1687         /**
1688          * Fired when either the drag pixel threshol or the mousedown hold
1689          * time threshold has been met.
1690          * @method startDrag
1691          * @param x {int} the X position of the original mousedown
1692          * @param y {int} the Y position of the original mousedown
1693          * @static
1694          */
1695         startDrag: function(x, y) {
1696             clearTimeout(this.clickTimeout);
1697             if (this.dragCurrent) {
1698                 this.dragCurrent.b4StartDrag(x, y);
1699                 this.dragCurrent.startDrag(x, y);
1700             }
1701             this.dragThreshMet = true;
1702         },
1703
1704         /**
1705          * Internal function to handle the mouseup event.  Will be invoked
1706          * from the context of the document.
1707          * @method handleMouseUp
1708          * @param {Event} e the event
1709          * @private
1710          * @static
1711          */
1712         handleMouseUp: function(e) {
1713
1714             if(Roo.QuickTips){
1715                 Roo.QuickTips.enable();
1716             }
1717             if (! this.dragCurrent) {
1718                 return;
1719             }
1720
1721             clearTimeout(this.clickTimeout);
1722
1723             if (this.dragThreshMet) {
1724                 this.fireEvents(e, true);
1725             } else {
1726             }
1727
1728             this.stopDrag(e);
1729
1730             this.stopEvent(e);
1731         },
1732
1733         /**
1734          * Utility to stop event propagation and event default, if these
1735          * features are turned on.
1736          * @method stopEvent
1737          * @param {Event} e the event as returned by this.getEvent()
1738          * @static
1739          */
1740         stopEvent: function(e){
1741             if(this.stopPropagation) {
1742                 e.stopPropagation();
1743             }
1744
1745             if (this.preventDefault) {
1746                 e.preventDefault();
1747             }
1748         },
1749
1750         /**
1751          * Internal function to clean up event handlers after the drag
1752          * operation is complete
1753          * @method stopDrag
1754          * @param {Event} e the event
1755          * @private
1756          * @static
1757          */
1758         stopDrag: function(e) {
1759             // Fire the drag end event for the item that was dragged
1760             if (this.dragCurrent) {
1761                 if (this.dragThreshMet) {
1762                     this.dragCurrent.b4EndDrag(e);
1763                     this.dragCurrent.endDrag(e);
1764                 }
1765
1766                 this.dragCurrent.onMouseUp(e);
1767             }
1768
1769             this.dragCurrent = null;
1770             this.dragOvers = {};
1771         },
1772
1773         /**
1774          * Internal function to handle the mousemove event.  Will be invoked
1775          * from the context of the html element.
1776          *
1777          * @TODO figure out what we can do about mouse events lost when the
1778          * user drags objects beyond the window boundary.  Currently we can
1779          * detect this in internet explorer by verifying that the mouse is
1780          * down during the mousemove event.  Firefox doesn't give us the
1781          * button state on the mousemove event.
1782          * @method handleMouseMove
1783          * @param {Event} e the event
1784          * @private
1785          * @static
1786          */
1787         handleMouseMove: function(e) {
1788             if (! this.dragCurrent) {
1789                 return true;
1790             }
1791
1792             // var button = e.which || e.button;
1793
1794             // check for IE mouseup outside of page boundary
1795             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
1796                 this.stopEvent(e);
1797                 return this.handleMouseUp(e);
1798             }
1799
1800             if (!this.dragThreshMet) {
1801                 var diffX = Math.abs(this.startX - e.getPageX());
1802                 var diffY = Math.abs(this.startY - e.getPageY());
1803                 if (diffX > this.clickPixelThresh ||
1804                             diffY > this.clickPixelThresh) {
1805                     this.startDrag(this.startX, this.startY);
1806                 }
1807             }
1808
1809             if (this.dragThreshMet) {
1810                 this.dragCurrent.b4Drag(e);
1811                 this.dragCurrent.onDrag(e);
1812                 if(!this.dragCurrent.moveOnly){
1813                     this.fireEvents(e, false);
1814                 }
1815             }
1816
1817             this.stopEvent(e);
1818
1819             return true;
1820         },
1821
1822         /**
1823          * Iterates over all of the DragDrop elements to find ones we are
1824          * hovering over or dropping on
1825          * @method fireEvents
1826          * @param {Event} e the event
1827          * @param {boolean} isDrop is this a drop op or a mouseover op?
1828          * @private
1829          * @static
1830          */
1831         fireEvents: function(e, isDrop) {
1832             var dc = this.dragCurrent;
1833
1834             // If the user did the mouse up outside of the window, we could
1835             // get here even though we have ended the drag.
1836             if (!dc || dc.isLocked()) {
1837                 return;
1838             }
1839
1840             var pt = e.getPoint();
1841
1842             // cache the previous dragOver array
1843             var oldOvers = [];
1844
1845             var outEvts   = [];
1846             var overEvts  = [];
1847             var dropEvts  = [];
1848             var enterEvts = [];
1849
1850             // Check to see if the object(s) we were hovering over is no longer
1851             // being hovered over so we can fire the onDragOut event
1852             for (var i in this.dragOvers) {
1853
1854                 var ddo = this.dragOvers[i];
1855
1856                 if (! this.isTypeOfDD(ddo)) {
1857                     continue;
1858                 }
1859
1860                 if (! this.isOverTarget(pt, ddo, this.mode)) {
1861                     outEvts.push( ddo );
1862                 }
1863
1864                 oldOvers[i] = true;
1865                 delete this.dragOvers[i];
1866             }
1867
1868             for (var sGroup in dc.groups) {
1869
1870                 if ("string" != typeof sGroup) {
1871                     continue;
1872                 }
1873
1874                 for (i in this.ids[sGroup]) {
1875                     var oDD = this.ids[sGroup][i];
1876                     if (! this.isTypeOfDD(oDD)) {
1877                         continue;
1878                     }
1879
1880                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
1881                         if (this.isOverTarget(pt, oDD, this.mode)) {
1882                             // look for drop interactions
1883                             if (isDrop) {
1884                                 dropEvts.push( oDD );
1885                             // look for drag enter and drag over interactions
1886                             } else {
1887
1888                                 // initial drag over: dragEnter fires
1889                                 if (!oldOvers[oDD.id]) {
1890                                     enterEvts.push( oDD );
1891                                 // subsequent drag overs: dragOver fires
1892                                 } else {
1893                                     overEvts.push( oDD );
1894                                 }
1895
1896                                 this.dragOvers[oDD.id] = oDD;
1897                             }
1898                         }
1899                     }
1900                 }
1901             }
1902
1903             if (this.mode) {
1904                 if (outEvts.length) {
1905                     dc.b4DragOut(e, outEvts);
1906                     dc.onDragOut(e, outEvts);
1907                 }
1908
1909                 if (enterEvts.length) {
1910                     dc.onDragEnter(e, enterEvts);
1911                 }
1912
1913                 if (overEvts.length) {
1914                     dc.b4DragOver(e, overEvts);
1915                     dc.onDragOver(e, overEvts);
1916                 }
1917
1918                 if (dropEvts.length) {
1919                     dc.b4DragDrop(e, dropEvts);
1920                     dc.onDragDrop(e, dropEvts);
1921                 }
1922
1923             } else {
1924                 // fire dragout events
1925                 var len = 0;
1926                 for (i=0, len=outEvts.length; i<len; ++i) {
1927                     dc.b4DragOut(e, outEvts[i].id);
1928                     dc.onDragOut(e, outEvts[i].id);
1929                 }
1930
1931                 // fire enter events
1932                 for (i=0,len=enterEvts.length; i<len; ++i) {
1933                     // dc.b4DragEnter(e, oDD.id);
1934                     dc.onDragEnter(e, enterEvts[i].id);
1935                 }
1936
1937                 // fire over events
1938                 for (i=0,len=overEvts.length; i<len; ++i) {
1939                     dc.b4DragOver(e, overEvts[i].id);
1940                     dc.onDragOver(e, overEvts[i].id);
1941                 }
1942
1943                 // fire drop events
1944                 for (i=0, len=dropEvts.length; i<len; ++i) {
1945                     dc.b4DragDrop(e, dropEvts[i].id);
1946                     dc.onDragDrop(e, dropEvts[i].id);
1947                 }
1948
1949             }
1950
1951             // notify about a drop that did not find a target
1952             if (isDrop && !dropEvts.length) {
1953                 dc.onInvalidDrop(e);
1954             }
1955
1956         },
1957
1958         /**
1959          * Helper function for getting the best match from the list of drag
1960          * and drop objects returned by the drag and drop events when we are
1961          * in INTERSECT mode.  It returns either the first object that the
1962          * cursor is over, or the object that has the greatest overlap with
1963          * the dragged element.
1964          * @method getBestMatch
1965          * @param  {DragDrop[]} dds The array of drag and drop objects
1966          * targeted
1967          * @return {DragDrop}       The best single match
1968          * @static
1969          */
1970         getBestMatch: function(dds) {
1971             var winner = null;
1972             // Return null if the input is not what we expect
1973             //if (!dds || !dds.length || dds.length == 0) {
1974                // winner = null;
1975             // If there is only one item, it wins
1976             //} else if (dds.length == 1) {
1977
1978             var len = dds.length;
1979
1980             if (len == 1) {
1981                 winner = dds[0];
1982             } else {
1983                 // Loop through the targeted items
1984                 for (var i=0; i<len; ++i) {
1985                     var dd = dds[i];
1986                     // If the cursor is over the object, it wins.  If the
1987                     // cursor is over multiple matches, the first one we come
1988                     // to wins.
1989                     if (dd.cursorIsOver) {
1990                         winner = dd;
1991                         break;
1992                     // Otherwise the object with the most overlap wins
1993                     } else {
1994                         if (!winner ||
1995                             winner.overlap.getArea() < dd.overlap.getArea()) {
1996                             winner = dd;
1997                         }
1998                     }
1999                 }
2000             }
2001
2002             return winner;
2003         },
2004
2005         /**
2006          * Refreshes the cache of the top-left and bottom-right points of the
2007          * drag and drop objects in the specified group(s).  This is in the
2008          * format that is stored in the drag and drop instance, so typical
2009          * usage is:
2010          * <code>
2011          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
2012          * </code>
2013          * Alternatively:
2014          * <code>
2015          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
2016          * </code>
2017          * @TODO this really should be an indexed array.  Alternatively this
2018          * method could accept both.
2019          * @method refreshCache
2020          * @param {Object} groups an associative array of groups to refresh
2021          * @static
2022          */
2023         refreshCache: function(groups) {
2024             for (var sGroup in groups) {
2025                 if ("string" != typeof sGroup) {
2026                     continue;
2027                 }
2028                 for (var i in this.ids[sGroup]) {
2029                     var oDD = this.ids[sGroup][i];
2030
2031                     if (this.isTypeOfDD(oDD)) {
2032                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
2033                         var loc = this.getLocation(oDD);
2034                         if (loc) {
2035                             this.locationCache[oDD.id] = loc;
2036                         } else {
2037                             delete this.locationCache[oDD.id];
2038                             // this will unregister the drag and drop object if
2039                             // the element is not in a usable state
2040                             // oDD.unreg();
2041                         }
2042                     }
2043                 }
2044             }
2045         },
2046
2047         /**
2048          * This checks to make sure an element exists and is in the DOM.  The
2049          * main purpose is to handle cases where innerHTML is used to remove
2050          * drag and drop objects from the DOM.  IE provides an 'unspecified
2051          * error' when trying to access the offsetParent of such an element
2052          * @method verifyEl
2053          * @param {HTMLElement} el the element to check
2054          * @return {boolean} true if the element looks usable
2055          * @static
2056          */
2057         verifyEl: function(el) {
2058             if (el) {
2059                 var parent;
2060                 if(Roo.isIE){
2061                     try{
2062                         parent = el.offsetParent;
2063                     }catch(e){}
2064                 }else{
2065                     parent = el.offsetParent;
2066                 }
2067                 if (parent) {
2068                     return true;
2069                 }
2070             }
2071
2072             return false;
2073         },
2074
2075         /**
2076          * Returns a Region object containing the drag and drop element's position
2077          * and size, including the padding configured for it
2078          * @method getLocation
2079          * @param {DragDrop} oDD the drag and drop object to get the
2080          *                       location for
2081          * @return {Roo.lib.Region} a Region object representing the total area
2082          *                             the element occupies, including any padding
2083          *                             the instance is configured for.
2084          * @static
2085          */
2086         getLocation: function(oDD) {
2087             if (! this.isTypeOfDD(oDD)) {
2088                 return null;
2089             }
2090
2091             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
2092
2093             try {
2094                 pos= Roo.lib.Dom.getXY(el);
2095             } catch (e) { }
2096
2097             if (!pos) {
2098                 return null;
2099             }
2100
2101             x1 = pos[0];
2102             x2 = x1 + el.offsetWidth;
2103             y1 = pos[1];
2104             y2 = y1 + el.offsetHeight;
2105
2106             t = y1 - oDD.padding[0];
2107             r = x2 + oDD.padding[1];
2108             b = y2 + oDD.padding[2];
2109             l = x1 - oDD.padding[3];
2110
2111             return new Roo.lib.Region( t, r, b, l );
2112         },
2113
2114         /**
2115          * Checks the cursor location to see if it over the target
2116          * @method isOverTarget
2117          * @param {Roo.lib.Point} pt The point to evaluate
2118          * @param {DragDrop} oTarget the DragDrop object we are inspecting
2119          * @return {boolean} true if the mouse is over the target
2120          * @private
2121          * @static
2122          */
2123         isOverTarget: function(pt, oTarget, intersect) {
2124             // use cache if available
2125             var loc = this.locationCache[oTarget.id];
2126             if (!loc || !this.useCache) {
2127                 loc = this.getLocation(oTarget);
2128                 this.locationCache[oTarget.id] = loc;
2129
2130             }
2131
2132             if (!loc) {
2133                 return false;
2134             }
2135
2136             oTarget.cursorIsOver = loc.contains( pt );
2137
2138             // DragDrop is using this as a sanity check for the initial mousedown
2139             // in this case we are done.  In POINT mode, if the drag obj has no
2140             // contraints, we are also done. Otherwise we need to evaluate the
2141             // location of the target as related to the actual location of the
2142             // dragged element.
2143             var dc = this.dragCurrent;
2144             if (!dc || !dc.getTargetCoord ||
2145                     (!intersect && !dc.constrainX && !dc.constrainY)) {
2146                 return oTarget.cursorIsOver;
2147             }
2148
2149             oTarget.overlap = null;
2150
2151             // Get the current location of the drag element, this is the
2152             // location of the mouse event less the delta that represents
2153             // where the original mousedown happened on the element.  We
2154             // need to consider constraints and ticks as well.
2155             var pos = dc.getTargetCoord(pt.x, pt.y);
2156
2157             var el = dc.getDragEl();
2158             var curRegion = new Roo.lib.Region( pos.y,
2159                                                    pos.x + el.offsetWidth,
2160                                                    pos.y + el.offsetHeight,
2161                                                    pos.x );
2162
2163             var overlap = curRegion.intersect(loc);
2164
2165             if (overlap) {
2166                 oTarget.overlap = overlap;
2167                 return (intersect) ? true : oTarget.cursorIsOver;
2168             } else {
2169                 return false;
2170             }
2171         },
2172
2173         /**
2174          * unload event handler
2175          * @method _onUnload
2176          * @private
2177          * @static
2178          */
2179         _onUnload: function(e, me) {
2180             Roo.dd.DragDropMgr.unregAll();
2181         },
2182
2183         /**
2184          * Cleans up the drag and drop events and objects.
2185          * @method unregAll
2186          * @private
2187          * @static
2188          */
2189         unregAll: function() {
2190
2191             if (this.dragCurrent) {
2192                 this.stopDrag();
2193                 this.dragCurrent = null;
2194             }
2195
2196             this._execOnAll("unreg", []);
2197
2198             for (i in this.elementCache) {
2199                 delete this.elementCache[i];
2200             }
2201
2202             this.elementCache = {};
2203             this.ids = {};
2204         },
2205
2206         /**
2207          * A cache of DOM elements
2208          * @property elementCache
2209          * @private
2210          * @static
2211          */
2212         elementCache: {},
2213
2214         /**
2215          * Get the wrapper for the DOM element specified
2216          * @method getElWrapper
2217          * @param {String} id the id of the element to get
2218          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
2219          * @private
2220          * @deprecated This wrapper isn't that useful
2221          * @static
2222          */
2223         getElWrapper: function(id) {
2224             var oWrapper = this.elementCache[id];
2225             if (!oWrapper || !oWrapper.el) {
2226                 oWrapper = this.elementCache[id] =
2227                     new this.ElementWrapper(Roo.getDom(id));
2228             }
2229             return oWrapper;
2230         },
2231
2232         /**
2233          * Returns the actual DOM element
2234          * @method getElement
2235          * @param {String} id the id of the elment to get
2236          * @return {Object} The element
2237          * @deprecated use Roo.getDom instead
2238          * @static
2239          */
2240         getElement: function(id) {
2241             return Roo.getDom(id);
2242         },
2243
2244         /**
2245          * Returns the style property for the DOM element (i.e.,
2246          * document.getElById(id).style)
2247          * @method getCss
2248          * @param {String} id the id of the elment to get
2249          * @return {Object} The style property of the element
2250          * @deprecated use Roo.getDom instead
2251          * @static
2252          */
2253         getCss: function(id) {
2254             var el = Roo.getDom(id);
2255             return (el) ? el.style : null;
2256         },
2257
2258         /**
2259          * Inner class for cached elements
2260          * @class DragDropMgr.ElementWrapper
2261          * @for DragDropMgr
2262          * @private
2263          * @deprecated
2264          */
2265         ElementWrapper: function(el) {
2266                 /**
2267                  * The element
2268                  * @property el
2269                  */
2270                 this.el = el || null;
2271                 /**
2272                  * The element id
2273                  * @property id
2274                  */
2275                 this.id = this.el && el.id;
2276                 /**
2277                  * A reference to the style property
2278                  * @property css
2279                  */
2280                 this.css = this.el && el.style;
2281             },
2282
2283         /**
2284          * Returns the X position of an html element
2285          * @method getPosX
2286          * @param el the element for which to get the position
2287          * @return {int} the X coordinate
2288          * @for DragDropMgr
2289          * @deprecated use Roo.lib.Dom.getX instead
2290          * @static
2291          */
2292         getPosX: function(el) {
2293             return Roo.lib.Dom.getX(el);
2294         },
2295
2296         /**
2297          * Returns the Y position of an html element
2298          * @method getPosY
2299          * @param el the element for which to get the position
2300          * @return {int} the Y coordinate
2301          * @deprecated use Roo.lib.Dom.getY instead
2302          * @static
2303          */
2304         getPosY: function(el) {
2305             return Roo.lib.Dom.getY(el);
2306         },
2307
2308         /**
2309          * Swap two nodes.  In IE, we use the native method, for others we
2310          * emulate the IE behavior
2311          * @method swapNode
2312          * @param n1 the first node to swap
2313          * @param n2 the other node to swap
2314          * @static
2315          */
2316         swapNode: function(n1, n2) {
2317             if (n1.swapNode) {
2318                 n1.swapNode(n2);
2319             } else {
2320                 var p = n2.parentNode;
2321                 var s = n2.nextSibling;
2322
2323                 if (s == n1) {
2324                     p.insertBefore(n1, n2);
2325                 } else if (n2 == n1.nextSibling) {
2326                     p.insertBefore(n2, n1);
2327                 } else {
2328                     n1.parentNode.replaceChild(n2, n1);
2329                     p.insertBefore(n1, s);
2330                 }
2331             }
2332         },
2333
2334         /**
2335          * Returns the current scroll position
2336          * @method getScroll
2337          * @private
2338          * @static
2339          */
2340         getScroll: function () {
2341             var t, l, dde=document.documentElement, db=document.body;
2342             if (dde && (dde.scrollTop || dde.scrollLeft)) {
2343                 t = dde.scrollTop;
2344                 l = dde.scrollLeft;
2345             } else if (db) {
2346                 t = db.scrollTop;
2347                 l = db.scrollLeft;
2348             } else {
2349
2350             }
2351             return { top: t, left: l };
2352         },
2353
2354         /**
2355          * Returns the specified element style property
2356          * @method getStyle
2357          * @param {HTMLElement} el          the element
2358          * @param {string}      styleProp   the style property
2359          * @return {string} The value of the style property
2360          * @deprecated use Roo.lib.Dom.getStyle
2361          * @static
2362          */
2363         getStyle: function(el, styleProp) {
2364             return Roo.fly(el).getStyle(styleProp);
2365         },
2366
2367         /**
2368          * Gets the scrollTop
2369          * @method getScrollTop
2370          * @return {int} the document's scrollTop
2371          * @static
2372          */
2373         getScrollTop: function () { return this.getScroll().top; },
2374
2375         /**
2376          * Gets the scrollLeft
2377          * @method getScrollLeft
2378          * @return {int} the document's scrollTop
2379          * @static
2380          */
2381         getScrollLeft: function () { return this.getScroll().left; },
2382
2383         /**
2384          * Sets the x/y position of an element to the location of the
2385          * target element.
2386          * @method moveToEl
2387          * @param {HTMLElement} moveEl      The element to move
2388          * @param {HTMLElement} targetEl    The position reference element
2389          * @static
2390          */
2391         moveToEl: function (moveEl, targetEl) {
2392             var aCoord = Roo.lib.Dom.getXY(targetEl);
2393             Roo.lib.Dom.setXY(moveEl, aCoord);
2394         },
2395
2396         /**
2397          * Numeric array sort function
2398          * @method numericSort
2399          * @static
2400          */
2401         numericSort: function(a, b) { return (a - b); },
2402
2403         /**
2404          * Internal counter
2405          * @property _timeoutCount
2406          * @private
2407          * @static
2408          */
2409         _timeoutCount: 0,
2410
2411         /**
2412          * Trying to make the load order less important.  Without this we get
2413          * an error if this file is loaded before the Event Utility.
2414          * @method _addListeners
2415          * @private
2416          * @static
2417          */
2418         _addListeners: function() {
2419             var DDM = Roo.dd.DDM;
2420             if ( Roo.lib.Event && document ) {
2421                 DDM._onLoad();
2422             } else {
2423                 if (DDM._timeoutCount > 2000) {
2424                 } else {
2425                     setTimeout(DDM._addListeners, 10);
2426                     if (document && document.body) {
2427                         DDM._timeoutCount += 1;
2428                     }
2429                 }
2430             }
2431         },
2432
2433         /**
2434          * Recursively searches the immediate parent and all child nodes for
2435          * the handle element in order to determine wheter or not it was
2436          * clicked.
2437          * @method handleWasClicked
2438          * @param node the html element to inspect
2439          * @static
2440          */
2441         handleWasClicked: function(node, id) {
2442             if (this.isHandle(id, node.id)) {
2443                 return true;
2444             } else {
2445                 // check to see if this is a text node child of the one we want
2446                 var p = node.parentNode;
2447
2448                 while (p) {
2449                     if (this.isHandle(id, p.id)) {
2450                         return true;
2451                     } else {
2452                         p = p.parentNode;
2453                     }
2454                 }
2455             }
2456
2457             return false;
2458         }
2459
2460     };
2461
2462 }();
2463
2464 // shorter alias, save a few bytes
2465 Roo.dd.DDM = Roo.dd.DragDropMgr;
2466 Roo.dd.DDM._addListeners();
2467
2468 }/*
2469  * Based on:
2470  * Ext JS Library 1.1.1
2471  * Copyright(c) 2006-2007, Ext JS, LLC.
2472  *
2473  * Originally Released Under LGPL - original licence link has changed is not relivant.
2474  *
2475  * Fork - LGPL
2476  * <script type="text/javascript">
2477  */
2478
2479 /**
2480  * @class Roo.dd.DD
2481  * A DragDrop implementation where the linked element follows the
2482  * mouse cursor during a drag.
2483  * @extends Roo.dd.DragDrop
2484  * @constructor
2485  * @param {String} id the id of the linked element
2486  * @param {String} sGroup the group of related DragDrop items
2487  * @param {object} config an object containing configurable attributes
2488  *                Valid properties for DD:
2489  *                    scroll
2490  */
2491 Roo.dd.DD = function(id, sGroup, config) {
2492     if (id) {
2493         this.init(id, sGroup, config);
2494     }
2495 };
2496
2497 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
2498
2499     /**
2500      * When set to true, the utility automatically tries to scroll the browser
2501      * window wehn a drag and drop element is dragged near the viewport boundary.
2502      * Defaults to true.
2503      * @property scroll
2504      * @type boolean
2505      */
2506     scroll: true,
2507
2508     /**
2509      * Sets the pointer offset to the distance between the linked element's top
2510      * left corner and the location the element was clicked
2511      * @method autoOffset
2512      * @param {int} iPageX the X coordinate of the click
2513      * @param {int} iPageY the Y coordinate of the click
2514      */
2515     autoOffset: function(iPageX, iPageY) {
2516         var x = iPageX - this.startPageX;
2517         var y = iPageY - this.startPageY;
2518         this.setDelta(x, y);
2519     },
2520
2521     /**
2522      * Sets the pointer offset.  You can call this directly to force the
2523      * offset to be in a particular location (e.g., pass in 0,0 to set it
2524      * to the center of the object)
2525      * @method setDelta
2526      * @param {int} iDeltaX the distance from the left
2527      * @param {int} iDeltaY the distance from the top
2528      */
2529     setDelta: function(iDeltaX, iDeltaY) {
2530         this.deltaX = iDeltaX;
2531         this.deltaY = iDeltaY;
2532     },
2533
2534     /**
2535      * Sets the drag element to the location of the mousedown or click event,
2536      * maintaining the cursor location relative to the location on the element
2537      * that was clicked.  Override this if you want to place the element in a
2538      * location other than where the cursor is.
2539      * @method setDragElPos
2540      * @param {int} iPageX the X coordinate of the mousedown or drag event
2541      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2542      */
2543     setDragElPos: function(iPageX, iPageY) {
2544         // the first time we do this, we are going to check to make sure
2545         // the element has css positioning
2546
2547         var el = this.getDragEl();
2548         this.alignElWithMouse(el, iPageX, iPageY);
2549     },
2550
2551     /**
2552      * Sets the element to the location of the mousedown or click event,
2553      * maintaining the cursor location relative to the location on the element
2554      * that was clicked.  Override this if you want to place the element in a
2555      * location other than where the cursor is.
2556      * @method alignElWithMouse
2557      * @param {HTMLElement} el the element to move
2558      * @param {int} iPageX the X coordinate of the mousedown or drag event
2559      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2560      */
2561     alignElWithMouse: function(el, iPageX, iPageY) {
2562         var oCoord = this.getTargetCoord(iPageX, iPageY);
2563         var fly = el.dom ? el : Roo.fly(el);
2564         if (!this.deltaSetXY) {
2565             var aCoord = [oCoord.x, oCoord.y];
2566             fly.setXY(aCoord);
2567             var newLeft = fly.getLeft(true);
2568             var newTop  = fly.getTop(true);
2569             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
2570         } else {
2571             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
2572         }
2573
2574         this.cachePosition(oCoord.x, oCoord.y);
2575         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
2576         return oCoord;
2577     },
2578
2579     /**
2580      * Saves the most recent position so that we can reset the constraints and
2581      * tick marks on-demand.  We need to know this so that we can calculate the
2582      * number of pixels the element is offset from its original position.
2583      * @method cachePosition
2584      * @param iPageX the current x position (optional, this just makes it so we
2585      * don't have to look it up again)
2586      * @param iPageY the current y position (optional, this just makes it so we
2587      * don't have to look it up again)
2588      */
2589     cachePosition: function(iPageX, iPageY) {
2590         if (iPageX) {
2591             this.lastPageX = iPageX;
2592             this.lastPageY = iPageY;
2593         } else {
2594             var aCoord = Roo.lib.Dom.getXY(this.getEl());
2595             this.lastPageX = aCoord[0];
2596             this.lastPageY = aCoord[1];
2597         }
2598     },
2599
2600     /**
2601      * Auto-scroll the window if the dragged object has been moved beyond the
2602      * visible window boundary.
2603      * @method autoScroll
2604      * @param {int} x the drag element's x position
2605      * @param {int} y the drag element's y position
2606      * @param {int} h the height of the drag element
2607      * @param {int} w the width of the drag element
2608      * @private
2609      */
2610     autoScroll: function(x, y, h, w) {
2611
2612         if (this.scroll) {
2613             // The client height
2614             var clientH = Roo.lib.Dom.getViewWidth();
2615
2616             // The client width
2617             var clientW = Roo.lib.Dom.getViewHeight();
2618
2619             // The amt scrolled down
2620             var st = this.DDM.getScrollTop();
2621
2622             // The amt scrolled right
2623             var sl = this.DDM.getScrollLeft();
2624
2625             // Location of the bottom of the element
2626             var bot = h + y;
2627
2628             // Location of the right of the element
2629             var right = w + x;
2630
2631             // The distance from the cursor to the bottom of the visible area,
2632             // adjusted so that we don't scroll if the cursor is beyond the
2633             // element drag constraints
2634             var toBot = (clientH + st - y - this.deltaY);
2635
2636             // The distance from the cursor to the right of the visible area
2637             var toRight = (clientW + sl - x - this.deltaX);
2638
2639
2640             // How close to the edge the cursor must be before we scroll
2641             // var thresh = (document.all) ? 100 : 40;
2642             var thresh = 40;
2643
2644             // How many pixels to scroll per autoscroll op.  This helps to reduce
2645             // clunky scrolling. IE is more sensitive about this ... it needs this
2646             // value to be higher.
2647             var scrAmt = (document.all) ? 80 : 30;
2648
2649             // Scroll down if we are near the bottom of the visible page and the
2650             // obj extends below the crease
2651             if ( bot > clientH && toBot < thresh ) {
2652                 window.scrollTo(sl, st + scrAmt);
2653             }
2654
2655             // Scroll up if the window is scrolled down and the top of the object
2656             // goes above the top border
2657             if ( y < st && st > 0 && y - st < thresh ) {
2658                 window.scrollTo(sl, st - scrAmt);
2659             }
2660
2661             // Scroll right if the obj is beyond the right border and the cursor is
2662             // near the border.
2663             if ( right > clientW && toRight < thresh ) {
2664                 window.scrollTo(sl + scrAmt, st);
2665             }
2666
2667             // Scroll left if the window has been scrolled to the right and the obj
2668             // extends past the left border
2669             if ( x < sl && sl > 0 && x - sl < thresh ) {
2670                 window.scrollTo(sl - scrAmt, st);
2671             }
2672         }
2673     },
2674
2675     /**
2676      * Finds the location the element should be placed if we want to move
2677      * it to where the mouse location less the click offset would place us.
2678      * @method getTargetCoord
2679      * @param {int} iPageX the X coordinate of the click
2680      * @param {int} iPageY the Y coordinate of the click
2681      * @return an object that contains the coordinates (Object.x and Object.y)
2682      * @private
2683      */
2684     getTargetCoord: function(iPageX, iPageY) {
2685
2686
2687         var x = iPageX - this.deltaX;
2688         var y = iPageY - this.deltaY;
2689
2690         if (this.constrainX) {
2691             if (x < this.minX) { x = this.minX; }
2692             if (x > this.maxX) { x = this.maxX; }
2693         }
2694
2695         if (this.constrainY) {
2696             if (y < this.minY) { y = this.minY; }
2697             if (y > this.maxY) { y = this.maxY; }
2698         }
2699
2700         x = this.getTick(x, this.xTicks);
2701         y = this.getTick(y, this.yTicks);
2702
2703
2704         return {x:x, y:y};
2705     },
2706
2707     /*
2708      * Sets up config options specific to this class. Overrides
2709      * Roo.dd.DragDrop, but all versions of this method through the
2710      * inheritance chain are called
2711      */
2712     applyConfig: function() {
2713         Roo.dd.DD.superclass.applyConfig.call(this);
2714         this.scroll = (this.config.scroll !== false);
2715     },
2716
2717     /*
2718      * Event that fires prior to the onMouseDown event.  Overrides
2719      * Roo.dd.DragDrop.
2720      */
2721     b4MouseDown: function(e) {
2722         // this.resetConstraints();
2723         this.autoOffset(e.getPageX(),
2724                             e.getPageY());
2725     },
2726
2727     /*
2728      * Event that fires prior to the onDrag event.  Overrides
2729      * Roo.dd.DragDrop.
2730      */
2731     b4Drag: function(e) {
2732         this.setDragElPos(e.getPageX(),
2733                             e.getPageY());
2734     },
2735
2736     toString: function() {
2737         return ("DD " + this.id);
2738     }
2739
2740     //////////////////////////////////////////////////////////////////////////
2741     // Debugging ygDragDrop events that can be overridden
2742     //////////////////////////////////////////////////////////////////////////
2743     /*
2744     startDrag: function(x, y) {
2745     },
2746
2747     onDrag: function(e) {
2748     },
2749
2750     onDragEnter: function(e, id) {
2751     },
2752
2753     onDragOver: function(e, id) {
2754     },
2755
2756     onDragOut: function(e, id) {
2757     },
2758
2759     onDragDrop: function(e, id) {
2760     },
2761
2762     endDrag: function(e) {
2763     }
2764
2765     */
2766
2767 });/*
2768  * Based on:
2769  * Ext JS Library 1.1.1
2770  * Copyright(c) 2006-2007, Ext JS, LLC.
2771  *
2772  * Originally Released Under LGPL - original licence link has changed is not relivant.
2773  *
2774  * Fork - LGPL
2775  * <script type="text/javascript">
2776  */
2777
2778 /**
2779  * @class Roo.dd.DDProxy
2780  * A DragDrop implementation that inserts an empty, bordered div into
2781  * the document that follows the cursor during drag operations.  At the time of
2782  * the click, the frame div is resized to the dimensions of the linked html
2783  * element, and moved to the exact location of the linked element.
2784  *
2785  * References to the "frame" element refer to the single proxy element that
2786  * was created to be dragged in place of all DDProxy elements on the
2787  * page.
2788  *
2789  * @extends Roo.dd.DD
2790  * @constructor
2791  * @param {String} id the id of the linked html element
2792  * @param {String} sGroup the group of related DragDrop objects
2793  * @param {object} config an object containing configurable attributes
2794  *                Valid properties for DDProxy in addition to those in DragDrop:
2795  *                   resizeFrame, centerFrame, dragElId
2796  */
2797 Roo.dd.DDProxy = function(id, sGroup, config) {
2798     if (id) {
2799         this.init(id, sGroup, config);
2800         this.initFrame();
2801     }
2802 };
2803
2804 /**
2805  * The default drag frame div id
2806  * @property Roo.dd.DDProxy.dragElId
2807  * @type String
2808  * @static
2809  */
2810 Roo.dd.DDProxy.dragElId = "ygddfdiv";
2811
2812 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
2813
2814     /**
2815      * By default we resize the drag frame to be the same size as the element
2816      * we want to drag (this is to get the frame effect).  We can turn it off
2817      * if we want a different behavior.
2818      * @property resizeFrame
2819      * @type boolean
2820      */
2821     resizeFrame: true,
2822
2823     /**
2824      * By default the frame is positioned exactly where the drag element is, so
2825      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
2826      * you do not have constraints on the obj is to have the drag frame centered
2827      * around the cursor.  Set centerFrame to true for this effect.
2828      * @property centerFrame
2829      * @type boolean
2830      */
2831     centerFrame: false,
2832
2833     /**
2834      * Creates the proxy element if it does not yet exist
2835      * @method createFrame
2836      */
2837     createFrame: function() {
2838         var self = this;
2839         var body = document.body;
2840
2841         if (!body || !body.firstChild) {
2842             setTimeout( function() { self.createFrame(); }, 50 );
2843             return;
2844         }
2845
2846         var div = this.getDragEl();
2847
2848         if (!div) {
2849             div    = document.createElement("div");
2850             div.id = this.dragElId;
2851             var s  = div.style;
2852
2853             s.position   = "absolute";
2854             s.visibility = "hidden";
2855             s.cursor     = "move";
2856             s.border     = "2px solid #aaa";
2857             s.zIndex     = 999;
2858
2859             // appendChild can blow up IE if invoked prior to the window load event
2860             // while rendering a table.  It is possible there are other scenarios
2861             // that would cause this to happen as well.
2862             body.insertBefore(div, body.firstChild);
2863         }
2864     },
2865
2866     /**
2867      * Initialization for the drag frame element.  Must be called in the
2868      * constructor of all subclasses
2869      * @method initFrame
2870      */
2871     initFrame: function() {
2872         this.createFrame();
2873     },
2874
2875     applyConfig: function() {
2876         Roo.dd.DDProxy.superclass.applyConfig.call(this);
2877
2878         this.resizeFrame = (this.config.resizeFrame !== false);
2879         this.centerFrame = (this.config.centerFrame);
2880         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
2881     },
2882
2883     /**
2884      * Resizes the drag frame to the dimensions of the clicked object, positions
2885      * it over the object, and finally displays it
2886      * @method showFrame
2887      * @param {int} iPageX X click position
2888      * @param {int} iPageY Y click position
2889      * @private
2890      */
2891     showFrame: function(iPageX, iPageY) {
2892         var el = this.getEl();
2893         var dragEl = this.getDragEl();
2894         var s = dragEl.style;
2895
2896         this._resizeProxy();
2897
2898         if (this.centerFrame) {
2899             this.setDelta( Math.round(parseInt(s.width,  10)/2),
2900                            Math.round(parseInt(s.height, 10)/2) );
2901         }
2902
2903         this.setDragElPos(iPageX, iPageY);
2904
2905         Roo.fly(dragEl).show();
2906     },
2907
2908     /**
2909      * The proxy is automatically resized to the dimensions of the linked
2910      * element when a drag is initiated, unless resizeFrame is set to false
2911      * @method _resizeProxy
2912      * @private
2913      */
2914     _resizeProxy: function() {
2915         if (this.resizeFrame) {
2916             var el = this.getEl();
2917             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
2918         }
2919     },
2920
2921     // overrides Roo.dd.DragDrop
2922     b4MouseDown: function(e) {
2923         var x = e.getPageX();
2924         var y = e.getPageY();
2925         this.autoOffset(x, y);
2926         this.setDragElPos(x, y);
2927     },
2928
2929     // overrides Roo.dd.DragDrop
2930     b4StartDrag: function(x, y) {
2931         // show the drag frame
2932         this.showFrame(x, y);
2933     },
2934
2935     // overrides Roo.dd.DragDrop
2936     b4EndDrag: function(e) {
2937         Roo.fly(this.getDragEl()).hide();
2938     },
2939
2940     // overrides Roo.dd.DragDrop
2941     // By default we try to move the element to the last location of the frame.
2942     // This is so that the default behavior mirrors that of Roo.dd.DD.
2943     endDrag: function(e) {
2944
2945         var lel = this.getEl();
2946         var del = this.getDragEl();
2947
2948         // Show the drag frame briefly so we can get its position
2949         del.style.visibility = "";
2950
2951         this.beforeMove();
2952         // Hide the linked element before the move to get around a Safari
2953         // rendering bug.
2954         lel.style.visibility = "hidden";
2955         Roo.dd.DDM.moveToEl(lel, del);
2956         del.style.visibility = "hidden";
2957         lel.style.visibility = "";
2958
2959         this.afterDrag();
2960     },
2961
2962     beforeMove : function(){
2963
2964     },
2965
2966     afterDrag : function(){
2967
2968     },
2969
2970     toString: function() {
2971         return ("DDProxy " + this.id);
2972     }
2973
2974 });
2975 /*
2976  * Based on:
2977  * Ext JS Library 1.1.1
2978  * Copyright(c) 2006-2007, Ext JS, LLC.
2979  *
2980  * Originally Released Under LGPL - original licence link has changed is not relivant.
2981  *
2982  * Fork - LGPL
2983  * <script type="text/javascript">
2984  */
2985
2986  /**
2987  * @class Roo.dd.DDTarget
2988  * A DragDrop implementation that does not move, but can be a drop
2989  * target.  You would get the same result by simply omitting implementation
2990  * for the event callbacks, but this way we reduce the processing cost of the
2991  * event listener and the callbacks.
2992  * @extends Roo.dd.DragDrop
2993  * @constructor
2994  * @param {String} id the id of the element that is a drop target
2995  * @param {String} sGroup the group of related DragDrop objects
2996  * @param {object} config an object containing configurable attributes
2997  *                 Valid properties for DDTarget in addition to those in
2998  *                 DragDrop:
2999  *                    none
3000  */
3001 Roo.dd.DDTarget = function(id, sGroup, config) {
3002     if (id) {
3003         this.initTarget(id, sGroup, config);
3004     }
3005     if (config.listeners || config.events) { 
3006        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
3007             listeners : config.listeners || {}, 
3008             events : config.events || {} 
3009         });    
3010     }
3011 };
3012
3013 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
3014 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
3015     toString: function() {
3016         return ("DDTarget " + this.id);
3017     }
3018 });
3019 /*
3020  * Based on:
3021  * Ext JS Library 1.1.1
3022  * Copyright(c) 2006-2007, Ext JS, LLC.
3023  *
3024  * Originally Released Under LGPL - original licence link has changed is not relivant.
3025  *
3026  * Fork - LGPL
3027  * <script type="text/javascript">
3028  */
3029  
3030
3031 /**
3032  * @class Roo.dd.ScrollManager
3033  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
3034  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
3035  * @singleton
3036  */
3037 Roo.dd.ScrollManager = function(){
3038     var ddm = Roo.dd.DragDropMgr;
3039     var els = {};
3040     var dragEl = null;
3041     var proc = {};
3042     
3043     
3044     
3045     var onStop = function(e){
3046         dragEl = null;
3047         clearProc();
3048     };
3049     
3050     var triggerRefresh = function(){
3051         if(ddm.dragCurrent){
3052              ddm.refreshCache(ddm.dragCurrent.groups);
3053         }
3054     };
3055     
3056     var doScroll = function(){
3057         if(ddm.dragCurrent){
3058             var dds = Roo.dd.ScrollManager;
3059             if(!dds.animate){
3060                 if(proc.el.scroll(proc.dir, dds.increment)){
3061                     triggerRefresh();
3062                 }
3063             }else{
3064                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
3065             }
3066         }
3067     };
3068     
3069     var clearProc = function(){
3070         if(proc.id){
3071             clearInterval(proc.id);
3072         }
3073         proc.id = 0;
3074         proc.el = null;
3075         proc.dir = "";
3076     };
3077     
3078     var startProc = function(el, dir){
3079          Roo.log('scroll startproc');
3080         clearProc();
3081         proc.el = el;
3082         proc.dir = dir;
3083         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
3084     };
3085     
3086     var onFire = function(e, isDrop){
3087        
3088         if(isDrop || !ddm.dragCurrent){ return; }
3089         var dds = Roo.dd.ScrollManager;
3090         if(!dragEl || dragEl != ddm.dragCurrent){
3091             dragEl = ddm.dragCurrent;
3092             // refresh regions on drag start
3093             dds.refreshCache();
3094         }
3095         
3096         var xy = Roo.lib.Event.getXY(e);
3097         var pt = new Roo.lib.Point(xy[0], xy[1]);
3098         for(var id in els){
3099             var el = els[id], r = el._region;
3100             if(r && r.contains(pt) && el.isScrollable()){
3101                 if(r.bottom - pt.y <= dds.thresh){
3102                     if(proc.el != el){
3103                         startProc(el, "down");
3104                     }
3105                     return;
3106                 }else if(r.right - pt.x <= dds.thresh){
3107                     if(proc.el != el){
3108                         startProc(el, "left");
3109                     }
3110                     return;
3111                 }else if(pt.y - r.top <= dds.thresh){
3112                     if(proc.el != el){
3113                         startProc(el, "up");
3114                     }
3115                     return;
3116                 }else if(pt.x - r.left <= dds.thresh){
3117                     if(proc.el != el){
3118                         startProc(el, "right");
3119                     }
3120                     return;
3121                 }
3122             }
3123         }
3124         clearProc();
3125     };
3126     
3127     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
3128     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
3129     
3130     return {
3131         /**
3132          * Registers new overflow element(s) to auto scroll
3133          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
3134          */
3135         register : function(el){
3136             if(el instanceof Array){
3137                 for(var i = 0, len = el.length; i < len; i++) {
3138                         this.register(el[i]);
3139                 }
3140             }else{
3141                 el = Roo.get(el);
3142                 els[el.id] = el;
3143             }
3144             Roo.dd.ScrollManager.els = els;
3145         },
3146         
3147         /**
3148          * Unregisters overflow element(s) so they are no longer scrolled
3149          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
3150          */
3151         unregister : function(el){
3152             if(el instanceof Array){
3153                 for(var i = 0, len = el.length; i < len; i++) {
3154                         this.unregister(el[i]);
3155                 }
3156             }else{
3157                 el = Roo.get(el);
3158                 delete els[el.id];
3159             }
3160         },
3161         
3162         /**
3163          * The number of pixels from the edge of a container the pointer needs to be to 
3164          * trigger scrolling (defaults to 25)
3165          * @type Number
3166          */
3167         thresh : 25,
3168         
3169         /**
3170          * The number of pixels to scroll in each scroll increment (defaults to 50)
3171          * @type Number
3172          */
3173         increment : 100,
3174         
3175         /**
3176          * The frequency of scrolls in milliseconds (defaults to 500)
3177          * @type Number
3178          */
3179         frequency : 500,
3180         
3181         /**
3182          * True to animate the scroll (defaults to true)
3183          * @type Boolean
3184          */
3185         animate: true,
3186         
3187         /**
3188          * The animation duration in seconds - 
3189          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
3190          * @type Number
3191          */
3192         animDuration: .4,
3193         
3194         /**
3195          * Manually trigger a cache refresh.
3196          */
3197         refreshCache : function(){
3198             for(var id in els){
3199                 if(typeof els[id] == 'object'){ // for people extending the object prototype
3200                     els[id]._region = els[id].getRegion();
3201                 }
3202             }
3203         }
3204     };
3205 }();/*
3206  * Based on:
3207  * Ext JS Library 1.1.1
3208  * Copyright(c) 2006-2007, Ext JS, LLC.
3209  *
3210  * Originally Released Under LGPL - original licence link has changed is not relivant.
3211  *
3212  * Fork - LGPL
3213  * <script type="text/javascript">
3214  */
3215  
3216
3217 /**
3218  * @class Roo.dd.Registry
3219  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
3220  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
3221  * @singleton
3222  */
3223 Roo.dd.Registry = function(){
3224     var elements = {}; 
3225     var handles = {}; 
3226     var autoIdSeed = 0;
3227
3228     var getId = function(el, autogen){
3229         if(typeof el == "string"){
3230             return el;
3231         }
3232         var id = el.id;
3233         if(!id && autogen !== false){
3234             id = "roodd-" + (++autoIdSeed);
3235             el.id = id;
3236         }
3237         return id;
3238     };
3239     
3240     return {
3241     /**
3242      * Register a drag drop element
3243      * @param {String|HTMLElement} element The id or DOM node to register
3244      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
3245      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
3246      * knows how to interpret, plus there are some specific properties known to the Registry that should be
3247      * populated in the data object (if applicable):
3248      * <pre>
3249 Value      Description<br />
3250 ---------  ------------------------------------------<br />
3251 handles    Array of DOM nodes that trigger dragging<br />
3252            for the element being registered<br />
3253 isHandle   True if the element passed in triggers<br />
3254            dragging itself, else false
3255 </pre>
3256      */
3257         register : function(el, data){
3258             data = data || {};
3259             if(typeof el == "string"){
3260                 el = document.getElementById(el);
3261             }
3262             data.ddel = el;
3263             elements[getId(el)] = data;
3264             if(data.isHandle !== false){
3265                 handles[data.ddel.id] = data;
3266             }
3267             if(data.handles){
3268                 var hs = data.handles;
3269                 for(var i = 0, len = hs.length; i < len; i++){
3270                         handles[getId(hs[i])] = data;
3271                 }
3272             }
3273         },
3274
3275     /**
3276      * Unregister a drag drop element
3277      * @param {String|HTMLElement}  element The id or DOM node to unregister
3278      */
3279         unregister : function(el){
3280             var id = getId(el, false);
3281             var data = elements[id];
3282             if(data){
3283                 delete elements[id];
3284                 if(data.handles){
3285                     var hs = data.handles;
3286                     for(var i = 0, len = hs.length; i < len; i++){
3287                         delete handles[getId(hs[i], false)];
3288                     }
3289                 }
3290             }
3291         },
3292
3293     /**
3294      * Returns the handle registered for a DOM Node by id
3295      * @param {String|HTMLElement} id The DOM node or id to look up
3296      * @return {Object} handle The custom handle data
3297      */
3298         getHandle : function(id){
3299             if(typeof id != "string"){ // must be element?
3300                 id = id.id;
3301             }
3302             return handles[id];
3303         },
3304
3305     /**
3306      * Returns the handle that is registered for the DOM node that is the target of the event
3307      * @param {Event} e The event
3308      * @return {Object} handle The custom handle data
3309      */
3310         getHandleFromEvent : function(e){
3311             var t = Roo.lib.Event.getTarget(e);
3312             return t ? handles[t.id] : null;
3313         },
3314
3315     /**
3316      * Returns a custom data object that is registered for a DOM node by id
3317      * @param {String|HTMLElement} id The DOM node or id to look up
3318      * @return {Object} data The custom data
3319      */
3320         getTarget : function(id){
3321             if(typeof id != "string"){ // must be element?
3322                 id = id.id;
3323             }
3324             return elements[id];
3325         },
3326
3327     /**
3328      * Returns a custom data object that is registered for the DOM node that is the target of the event
3329      * @param {Event} e The event
3330      * @return {Object} data The custom data
3331      */
3332         getTargetFromEvent : function(e){
3333             var t = Roo.lib.Event.getTarget(e);
3334             return t ? elements[t.id] || handles[t.id] : null;
3335         }
3336     };
3337 }();/*
3338  * Based on:
3339  * Ext JS Library 1.1.1
3340  * Copyright(c) 2006-2007, Ext JS, LLC.
3341  *
3342  * Originally Released Under LGPL - original licence link has changed is not relivant.
3343  *
3344  * Fork - LGPL
3345  * <script type="text/javascript">
3346  */
3347  
3348
3349 /**
3350  * @class Roo.dd.StatusProxy
3351  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
3352  * default drag proxy used by all Roo.dd components.
3353  * @constructor
3354  * @param {Object} config
3355  */
3356 Roo.dd.StatusProxy = function(config){
3357     Roo.apply(this, config);
3358     this.id = this.id || Roo.id();
3359     this.el = new Roo.Layer({
3360         dh: {
3361             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
3362                 {tag: "div", cls: "x-dd-drop-icon"},
3363                 {tag: "div", cls: "x-dd-drag-ghost"}
3364             ]
3365         }, 
3366         shadow: !config || config.shadow !== false
3367     });
3368     this.ghost = Roo.get(this.el.dom.childNodes[1]);
3369     this.dropStatus = this.dropNotAllowed;
3370 };
3371
3372 Roo.dd.StatusProxy.prototype = {
3373     /**
3374      * @cfg {String} dropAllowed
3375      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
3376      */
3377     dropAllowed : "x-dd-drop-ok",
3378     /**
3379      * @cfg {String} dropNotAllowed
3380      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
3381      */
3382     dropNotAllowed : "x-dd-drop-nodrop",
3383
3384     /**
3385      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
3386      * over the current target element.
3387      * @param {String} cssClass The css class for the new drop status indicator image
3388      */
3389     setStatus : function(cssClass){
3390         cssClass = cssClass || this.dropNotAllowed;
3391         if(this.dropStatus != cssClass){
3392             this.el.replaceClass(this.dropStatus, cssClass);
3393             this.dropStatus = cssClass;
3394         }
3395     },
3396
3397     /**
3398      * Resets the status indicator to the default dropNotAllowed value
3399      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
3400      */
3401     reset : function(clearGhost){
3402         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
3403         this.dropStatus = this.dropNotAllowed;
3404         if(clearGhost){
3405             this.ghost.update("");
3406         }
3407     },
3408
3409     /**
3410      * Updates the contents of the ghost element
3411      * @param {String} html The html that will replace the current innerHTML of the ghost element
3412      */
3413     update : function(html){
3414         if(typeof html == "string"){
3415             this.ghost.update(html);
3416         }else{
3417             this.ghost.update("");
3418             html.style.margin = "0";
3419             this.ghost.dom.appendChild(html);
3420         }
3421         // ensure float = none set?? cant remember why though.
3422         var el = this.ghost.dom.firstChild;
3423                 if(el){
3424                         Roo.fly(el).setStyle('float', 'none');
3425                 }
3426     },
3427     
3428     /**
3429      * Returns the underlying proxy {@link Roo.Layer}
3430      * @return {Roo.Layer} el
3431     */
3432     getEl : function(){
3433         return this.el;
3434     },
3435
3436     /**
3437      * Returns the ghost element
3438      * @return {Roo.Element} el
3439      */
3440     getGhost : function(){
3441         return this.ghost;
3442     },
3443
3444     /**
3445      * Hides the proxy
3446      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
3447      */
3448     hide : function(clear){
3449         this.el.hide();
3450         if(clear){
3451             this.reset(true);
3452         }
3453     },
3454
3455     /**
3456      * Stops the repair animation if it's currently running
3457      */
3458     stop : function(){
3459         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
3460             this.anim.stop();
3461         }
3462     },
3463
3464     /**
3465      * Displays this proxy
3466      */
3467     show : function(){
3468         this.el.show();
3469     },
3470
3471     /**
3472      * Force the Layer to sync its shadow and shim positions to the element
3473      */
3474     sync : function(){
3475         this.el.sync();
3476     },
3477
3478     /**
3479      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
3480      * invalid drop operation by the item being dragged.
3481      * @param {Array} xy The XY position of the element ([x, y])
3482      * @param {Function} callback The function to call after the repair is complete
3483      * @param {Object} scope The scope in which to execute the callback
3484      */
3485     repair : function(xy, callback, scope){
3486         this.callback = callback;
3487         this.scope = scope;
3488         if(xy && this.animRepair !== false){
3489             this.el.addClass("x-dd-drag-repair");
3490             this.el.hideUnders(true);
3491             this.anim = this.el.shift({
3492                 duration: this.repairDuration || .5,
3493                 easing: 'easeOut',
3494                 xy: xy,
3495                 stopFx: true,
3496                 callback: this.afterRepair,
3497                 scope: this
3498             });
3499         }else{
3500             this.afterRepair();
3501         }
3502     },
3503
3504     // private
3505     afterRepair : function(){
3506         this.hide(true);
3507         if(typeof this.callback == "function"){
3508             this.callback.call(this.scope || this);
3509         }
3510         this.callback = null;
3511         this.scope = null;
3512     }
3513 };/*
3514  * Based on:
3515  * Ext JS Library 1.1.1
3516  * Copyright(c) 2006-2007, Ext JS, LLC.
3517  *
3518  * Originally Released Under LGPL - original licence link has changed is not relivant.
3519  *
3520  * Fork - LGPL
3521  * <script type="text/javascript">
3522  */
3523
3524 /**
3525  * @class Roo.dd.DragSource
3526  * @extends Roo.dd.DDProxy
3527  * A simple class that provides the basic implementation needed to make any element draggable.
3528  * @constructor
3529  * @param {String/HTMLElement/Element} el The container element
3530  * @param {Object} config
3531  */
3532 Roo.dd.DragSource = function(el, config){
3533     this.el = Roo.get(el);
3534     this.dragData = {};
3535     
3536     Roo.apply(this, config);
3537     
3538     if(!this.proxy){
3539         this.proxy = new Roo.dd.StatusProxy();
3540     }
3541
3542     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
3543           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
3544     
3545     this.dragging = false;
3546 };
3547
3548 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
3549     /**
3550      * @cfg {String} dropAllowed
3551      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3552      */
3553     dropAllowed : "x-dd-drop-ok",
3554     /**
3555      * @cfg {String} dropNotAllowed
3556      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3557      */
3558     dropNotAllowed : "x-dd-drop-nodrop",
3559
3560     /**
3561      * Returns the data object associated with this drag source
3562      * @return {Object} data An object containing arbitrary data
3563      */
3564     getDragData : function(e){
3565         return this.dragData;
3566     },
3567
3568     // private
3569     onDragEnter : function(e, id){
3570         var target = Roo.dd.DragDropMgr.getDDById(id);
3571         this.cachedTarget = target;
3572         if(this.beforeDragEnter(target, e, id) !== false){
3573             if(target.isNotifyTarget){
3574                 var status = target.notifyEnter(this, e, this.dragData);
3575                 this.proxy.setStatus(status);
3576             }else{
3577                 this.proxy.setStatus(this.dropAllowed);
3578             }
3579             
3580             if(this.afterDragEnter){
3581                 /**
3582                  * An empty function by default, but provided so that you can perform a custom action
3583                  * when the dragged item enters the drop target by providing an implementation.
3584                  * @param {Roo.dd.DragDrop} target The drop target
3585                  * @param {Event} e The event object
3586                  * @param {String} id The id of the dragged element
3587                  * @method afterDragEnter
3588                  */
3589                 this.afterDragEnter(target, e, id);
3590             }
3591         }
3592     },
3593
3594     /**
3595      * An empty function by default, but provided so that you can perform a custom action
3596      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
3597      * @param {Roo.dd.DragDrop} target The drop target
3598      * @param {Event} e The event object
3599      * @param {String} id The id of the dragged element
3600      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3601      */
3602     beforeDragEnter : function(target, e, id){
3603         return true;
3604     },
3605
3606     // private
3607     alignElWithMouse: function() {
3608         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
3609         this.proxy.sync();
3610     },
3611
3612     // private
3613     onDragOver : function(e, id){
3614         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3615         if(this.beforeDragOver(target, e, id) !== false){
3616             if(target.isNotifyTarget){
3617                 var status = target.notifyOver(this, e, this.dragData);
3618                 this.proxy.setStatus(status);
3619             }
3620
3621             if(this.afterDragOver){
3622                 /**
3623                  * An empty function by default, but provided so that you can perform a custom action
3624                  * while the dragged item is over the drop target by providing an implementation.
3625                  * @param {Roo.dd.DragDrop} target The drop target
3626                  * @param {Event} e The event object
3627                  * @param {String} id The id of the dragged element
3628                  * @method afterDragOver
3629                  */
3630                 this.afterDragOver(target, e, id);
3631             }
3632         }
3633     },
3634
3635     /**
3636      * An empty function by default, but provided so that you can perform a custom action
3637      * while the dragged item is over the drop target and optionally cancel the onDragOver.
3638      * @param {Roo.dd.DragDrop} target The drop target
3639      * @param {Event} e The event object
3640      * @param {String} id The id of the dragged element
3641      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3642      */
3643     beforeDragOver : function(target, e, id){
3644         return true;
3645     },
3646
3647     // private
3648     onDragOut : function(e, id){
3649         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3650         if(this.beforeDragOut(target, e, id) !== false){
3651             if(target.isNotifyTarget){
3652                 target.notifyOut(this, e, this.dragData);
3653             }
3654             this.proxy.reset();
3655             if(this.afterDragOut){
3656                 /**
3657                  * An empty function by default, but provided so that you can perform a custom action
3658                  * after the dragged item is dragged out of the target without dropping.
3659                  * @param {Roo.dd.DragDrop} target The drop target
3660                  * @param {Event} e The event object
3661                  * @param {String} id The id of the dragged element
3662                  * @method afterDragOut
3663                  */
3664                 this.afterDragOut(target, e, id);
3665             }
3666         }
3667         this.cachedTarget = null;
3668     },
3669
3670     /**
3671      * An empty function by default, but provided so that you can perform a custom action before the dragged
3672      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
3673      * @param {Roo.dd.DragDrop} target The drop target
3674      * @param {Event} e The event object
3675      * @param {String} id The id of the dragged element
3676      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3677      */
3678     beforeDragOut : function(target, e, id){
3679         return true;
3680     },
3681     
3682     // private
3683     onDragDrop : function(e, id){
3684         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3685         if(this.beforeDragDrop(target, e, id) !== false){
3686             if(target.isNotifyTarget){
3687                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
3688                     this.onValidDrop(target, e, id);
3689                 }else{
3690                     this.onInvalidDrop(target, e, id);
3691                 }
3692             }else{
3693                 this.onValidDrop(target, e, id);
3694             }
3695             
3696             if(this.afterDragDrop){
3697                 /**
3698                  * An empty function by default, but provided so that you can perform a custom action
3699                  * after a valid drag drop has occurred by providing an implementation.
3700                  * @param {Roo.dd.DragDrop} target The drop target
3701                  * @param {Event} e The event object
3702                  * @param {String} id The id of the dropped element
3703                  * @method afterDragDrop
3704                  */
3705                 this.afterDragDrop(target, e, id);
3706             }
3707         }
3708         delete this.cachedTarget;
3709     },
3710
3711     /**
3712      * An empty function by default, but provided so that you can perform a custom action before the dragged
3713      * item is dropped onto the target and optionally cancel the onDragDrop.
3714      * @param {Roo.dd.DragDrop} target The drop target
3715      * @param {Event} e The event object
3716      * @param {String} id The id of the dragged element
3717      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
3718      */
3719     beforeDragDrop : function(target, e, id){
3720         return true;
3721     },
3722
3723     // private
3724     onValidDrop : function(target, e, id){
3725         this.hideProxy();
3726         if(this.afterValidDrop){
3727             /**
3728              * An empty function by default, but provided so that you can perform a custom action
3729              * after a valid drop has occurred by providing an implementation.
3730              * @param {Object} target The target DD 
3731              * @param {Event} e The event object
3732              * @param {String} id The id of the dropped element
3733              * @method afterInvalidDrop
3734              */
3735             this.afterValidDrop(target, e, id);
3736         }
3737     },
3738
3739     // private
3740     getRepairXY : function(e, data){
3741         return this.el.getXY();  
3742     },
3743
3744     // private
3745     onInvalidDrop : function(target, e, id){
3746         this.beforeInvalidDrop(target, e, id);
3747         if(this.cachedTarget){
3748             if(this.cachedTarget.isNotifyTarget){
3749                 this.cachedTarget.notifyOut(this, e, this.dragData);
3750             }
3751             this.cacheTarget = null;
3752         }
3753         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
3754
3755         if(this.afterInvalidDrop){
3756             /**
3757              * An empty function by default, but provided so that you can perform a custom action
3758              * after an invalid drop has occurred by providing an implementation.
3759              * @param {Event} e The event object
3760              * @param {String} id The id of the dropped element
3761              * @method afterInvalidDrop
3762              */
3763             this.afterInvalidDrop(e, id);
3764         }
3765     },
3766
3767     // private
3768     afterRepair : function(){
3769         if(Roo.enableFx){
3770             this.el.highlight(this.hlColor || "c3daf9");
3771         }
3772         this.dragging = false;
3773     },
3774
3775     /**
3776      * An empty function by default, but provided so that you can perform a custom action after an invalid
3777      * drop has occurred.
3778      * @param {Roo.dd.DragDrop} target The drop target
3779      * @param {Event} e The event object
3780      * @param {String} id The id of the dragged element
3781      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
3782      */
3783     beforeInvalidDrop : function(target, e, id){
3784         return true;
3785     },
3786
3787     // private
3788     handleMouseDown : function(e){
3789         if(this.dragging) {
3790             return;
3791         }
3792         var data = this.getDragData(e);
3793         if(data && this.onBeforeDrag(data, e) !== false){
3794             this.dragData = data;
3795             this.proxy.stop();
3796             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
3797         } 
3798     },
3799
3800     /**
3801      * An empty function by default, but provided so that you can perform a custom action before the initial
3802      * drag event begins and optionally cancel it.
3803      * @param {Object} data An object containing arbitrary data to be shared with drop targets
3804      * @param {Event} e The event object
3805      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3806      */
3807     onBeforeDrag : function(data, e){
3808         return true;
3809     },
3810
3811     /**
3812      * An empty function by default, but provided so that you can perform a custom action once the initial
3813      * drag event has begun.  The drag cannot be canceled from this function.
3814      * @param {Number} x The x position of the click on the dragged object
3815      * @param {Number} y The y position of the click on the dragged object
3816      */
3817     onStartDrag : Roo.emptyFn,
3818
3819     // private - YUI override
3820     startDrag : function(x, y){
3821         this.proxy.reset();
3822         this.dragging = true;
3823         this.proxy.update("");
3824         this.onInitDrag(x, y);
3825         this.proxy.show();
3826     },
3827
3828     // private
3829     onInitDrag : function(x, y){
3830         var clone = this.el.dom.cloneNode(true);
3831         clone.id = Roo.id(); // prevent duplicate ids
3832         this.proxy.update(clone);
3833         this.onStartDrag(x, y);
3834         return true;
3835     },
3836
3837     /**
3838      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
3839      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
3840      */
3841     getProxy : function(){
3842         return this.proxy;  
3843     },
3844
3845     /**
3846      * Hides the drag source's {@link Roo.dd.StatusProxy}
3847      */
3848     hideProxy : function(){
3849         this.proxy.hide();  
3850         this.proxy.reset(true);
3851         this.dragging = false;
3852     },
3853
3854     // private
3855     triggerCacheRefresh : function(){
3856         Roo.dd.DDM.refreshCache(this.groups);
3857     },
3858
3859     // private - override to prevent hiding
3860     b4EndDrag: function(e) {
3861     },
3862
3863     // private - override to prevent moving
3864     endDrag : function(e){
3865         this.onEndDrag(this.dragData, e);
3866     },
3867
3868     // private
3869     onEndDrag : function(data, e){
3870     },
3871     
3872     // private - pin to cursor
3873     autoOffset : function(x, y) {
3874         this.setDelta(-12, -20);
3875     }    
3876 });/*
3877  * Based on:
3878  * Ext JS Library 1.1.1
3879  * Copyright(c) 2006-2007, Ext JS, LLC.
3880  *
3881  * Originally Released Under LGPL - original licence link has changed is not relivant.
3882  *
3883  * Fork - LGPL
3884  * <script type="text/javascript">
3885  */
3886
3887
3888 /**
3889  * @class Roo.dd.DropTarget
3890  * @extends Roo.dd.DDTarget
3891  * A simple class that provides the basic implementation needed to make any element a drop target that can have
3892  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
3893  * @constructor
3894  * @param {String/HTMLElement/Element} el The container element
3895  * @param {Object} config
3896  */
3897 Roo.dd.DropTarget = function(el, config){
3898     this.el = Roo.get(el);
3899     
3900     var listeners = false; ;
3901     if (config && config.listeners) {
3902         listeners= config.listeners;
3903         delete config.listeners;
3904     }
3905     Roo.apply(this, config);
3906     
3907     if(this.containerScroll){
3908         Roo.dd.ScrollManager.register(this.el);
3909     }
3910     this.addEvents( {
3911          /**
3912          * @scope Roo.dd.DropTarget
3913          */
3914          
3915          /**
3916          * @event enter
3917          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
3918          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
3919          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
3920          * 
3921          * IMPORTANT : it should set this.overClass and this.dropAllowed
3922          * 
3923          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3924          * @param {Event} e The event
3925          * @param {Object} data An object containing arbitrary data supplied by the drag source
3926          */
3927         "enter" : true,
3928         
3929          /**
3930          * @event over
3931          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
3932          * This method will be called on every mouse movement while the drag source is over the drop target.
3933          * This default implementation simply returns the dropAllowed config value.
3934          * 
3935          * IMPORTANT : it should set this.dropAllowed
3936          * 
3937          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3938          * @param {Event} e The event
3939          * @param {Object} data An object containing arbitrary data supplied by the drag source
3940          
3941          */
3942         "over" : true,
3943         /**
3944          * @event out
3945          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
3946          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
3947          * overClass (if any) from the drop element.
3948          * 
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 load
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         load : true,
4822         /**
4823          * @event loadexception
4824          * Fires if an exception occurs in the Proxy during loading.
4825          * Called with the signature of the Proxy's "loadexception" event.
4826          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
4827          * 
4828          * @param {Proxy} 
4829          * @param {Object} return from JsonData.reader() - success, totalRecords, records
4830          * @param {Object} load options 
4831          * @param {Object} jsonData from your request (normally this contains the Exception)
4832          */
4833         loadexception : true
4834     });
4835     
4836     if(this.proxy){
4837         this.proxy = Roo.factory(this.proxy, Roo.data);
4838         this.proxy.xmodule = this.xmodule || false;
4839         this.relayEvents(this.proxy,  ["loadexception"]);
4840     }
4841     this.sortToggle = {};
4842     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
4843
4844     Roo.data.Store.superclass.constructor.call(this);
4845
4846     if(this.inlineData){
4847         this.loadData(this.inlineData);
4848         delete this.inlineData;
4849     }
4850 };
4851 Roo.extend(Roo.data.Store, Roo.util.Observable, {
4852      /**
4853     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
4854     * without a remote query - used by combo/forms at present.
4855     */
4856     
4857     /**
4858     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
4859     */
4860     /**
4861     * @cfg {Array} data Inline data to be loaded when the store is initialized.
4862     */
4863     /**
4864     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
4865     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
4866     */
4867     /**
4868     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
4869     * on any HTTP request
4870     */
4871     /**
4872     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
4873     */
4874     /**
4875     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
4876     */
4877     multiSort: false,
4878     /**
4879     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
4880     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
4881     */
4882     remoteSort : false,
4883
4884     /**
4885     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
4886      * loaded or when a record is removed. (defaults to false).
4887     */
4888     pruneModifiedRecords : false,
4889
4890     // private
4891     lastOptions : null,
4892
4893     /**
4894      * Add Records to the Store and fires the add event.
4895      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4896      */
4897     add : function(records){
4898         records = [].concat(records);
4899         for(var i = 0, len = records.length; i < len; i++){
4900             records[i].join(this);
4901         }
4902         var index = this.data.length;
4903         this.data.addAll(records);
4904         this.fireEvent("add", this, records, index);
4905     },
4906
4907     /**
4908      * Remove a Record from the Store and fires the remove event.
4909      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
4910      */
4911     remove : function(record){
4912         var index = this.data.indexOf(record);
4913         this.data.removeAt(index);
4914         if(this.pruneModifiedRecords){
4915             this.modified.remove(record);
4916         }
4917         this.fireEvent("remove", this, record, index);
4918     },
4919
4920     /**
4921      * Remove all Records from the Store and fires the clear event.
4922      */
4923     removeAll : function(){
4924         this.data.clear();
4925         if(this.pruneModifiedRecords){
4926             this.modified = [];
4927         }
4928         this.fireEvent("clear", this);
4929     },
4930
4931     /**
4932      * Inserts Records to the Store at the given index and fires the add event.
4933      * @param {Number} index The start index at which to insert the passed Records.
4934      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4935      */
4936     insert : function(index, records){
4937         records = [].concat(records);
4938         for(var i = 0, len = records.length; i < len; i++){
4939             this.data.insert(index, records[i]);
4940             records[i].join(this);
4941         }
4942         this.fireEvent("add", this, records, index);
4943     },
4944
4945     /**
4946      * Get the index within the cache of the passed Record.
4947      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
4948      * @return {Number} The index of the passed Record. Returns -1 if not found.
4949      */
4950     indexOf : function(record){
4951         return this.data.indexOf(record);
4952     },
4953
4954     /**
4955      * Get the index within the cache of the Record with the passed id.
4956      * @param {String} id The id of the Record to find.
4957      * @return {Number} The index of the Record. Returns -1 if not found.
4958      */
4959     indexOfId : function(id){
4960         return this.data.indexOfKey(id);
4961     },
4962
4963     /**
4964      * Get the Record with the specified id.
4965      * @param {String} id The id of the Record to find.
4966      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
4967      */
4968     getById : function(id){
4969         return this.data.key(id);
4970     },
4971
4972     /**
4973      * Get the Record at the specified index.
4974      * @param {Number} index The index of the Record to find.
4975      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
4976      */
4977     getAt : function(index){
4978         return this.data.itemAt(index);
4979     },
4980
4981     /**
4982      * Returns a range of Records between specified indices.
4983      * @param {Number} startIndex (optional) The starting index (defaults to 0)
4984      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
4985      * @return {Roo.data.Record[]} An array of Records
4986      */
4987     getRange : function(start, end){
4988         return this.data.getRange(start, end);
4989     },
4990
4991     // private
4992     storeOptions : function(o){
4993         o = Roo.apply({}, o);
4994         delete o.callback;
4995         delete o.scope;
4996         this.lastOptions = o;
4997     },
4998
4999     /**
5000      * Loads the Record cache from the configured Proxy using the configured Reader.
5001      * <p>
5002      * If using remote paging, then the first load call must specify the <em>start</em>
5003      * and <em>limit</em> properties in the options.params property to establish the initial
5004      * position within the dataset, and the number of Records to cache on each read from the Proxy.
5005      * <p>
5006      * <strong>It is important to note that for remote data sources, loading is asynchronous,
5007      * and this call will return before the new data has been loaded. Perform any post-processing
5008      * in a callback function, or in a "load" event handler.</strong>
5009      * <p>
5010      * @param {Object} options An object containing properties which control loading options:<ul>
5011      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
5012      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
5013      * passed the following arguments:<ul>
5014      * <li>r : Roo.data.Record[]</li>
5015      * <li>options: Options object from the load call</li>
5016      * <li>success: Boolean success indicator</li></ul></li>
5017      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
5018      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
5019      * </ul>
5020      */
5021     load : function(options){
5022         options = options || {};
5023         if(this.fireEvent("beforeload", this, options) !== false){
5024             this.storeOptions(options);
5025             var p = Roo.apply(options.params || {}, this.baseParams);
5026             // if meta was not loaded from remote source.. try requesting it.
5027             if (!this.reader.metaFromRemote) {
5028                 p._requestMeta = 1;
5029             }
5030             if(this.sortInfo && this.remoteSort){
5031                 var pn = this.paramNames;
5032                 p[pn["sort"]] = this.sortInfo.field;
5033                 p[pn["dir"]] = this.sortInfo.direction;
5034             }
5035             if (this.multiSort) {
5036                 var pn = this.paramNames;
5037                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
5038             }
5039             
5040             this.proxy.load(p, this.reader, this.loadRecords, this, options);
5041         }
5042     },
5043
5044     /**
5045      * Reloads the Record cache from the configured Proxy using the configured Reader and
5046      * the options from the last load operation performed.
5047      * @param {Object} options (optional) An object containing properties which may override the options
5048      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
5049      * the most recently used options are reused).
5050      */
5051     reload : function(options){
5052         this.load(Roo.applyIf(options||{}, this.lastOptions));
5053     },
5054
5055     // private
5056     // Called as a callback by the Reader during a load operation.
5057     loadRecords : function(o, options, success){
5058         if(!o || success === false){
5059             if(success !== false){
5060                 this.fireEvent("load", this, [], options);
5061             }
5062             if(options.callback){
5063                 options.callback.call(options.scope || this, [], options, false);
5064             }
5065             return;
5066         }
5067         // if data returned failure - throw an exception.
5068         if (o.success === false) {
5069             // show a message if no listener is registered.
5070             if (!this.hasListener('loadexception') && typeof(this.reader.jsonData.errorMsg) != 'undefined') {
5071                     Roo.MessageBox.alert("Error loading",this.reader.jsonData.errorMsg);
5072             }
5073             // loadmask wil be hooked into this..
5074             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
5075             return;
5076         }
5077         var r = o.records, t = o.totalRecords || r.length;
5078         if(!options || options.add !== true){
5079             if(this.pruneModifiedRecords){
5080                 this.modified = [];
5081             }
5082             for(var i = 0, len = r.length; i < len; i++){
5083                 r[i].join(this);
5084             }
5085             if(this.snapshot){
5086                 this.data = this.snapshot;
5087                 delete this.snapshot;
5088             }
5089             this.data.clear();
5090             this.data.addAll(r);
5091             this.totalLength = t;
5092             this.applySort();
5093             this.fireEvent("datachanged", this);
5094         }else{
5095             this.totalLength = Math.max(t, this.data.length+r.length);
5096             this.add(r);
5097         }
5098         this.fireEvent("load", this, r, options);
5099         if(options.callback){
5100             options.callback.call(options.scope || this, r, options, true);
5101         }
5102     },
5103
5104
5105     /**
5106      * Loads data from a passed data block. A Reader which understands the format of the data
5107      * must have been configured in the constructor.
5108      * @param {Object} data The data block from which to read the Records.  The format of the data expected
5109      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
5110      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
5111      */
5112     loadData : function(o, append){
5113         var r = this.reader.readRecords(o);
5114         this.loadRecords(r, {add: append}, true);
5115     },
5116
5117     /**
5118      * Gets the number of cached records.
5119      * <p>
5120      * <em>If using paging, this may not be the total size of the dataset. If the data object
5121      * used by the Reader contains the dataset size, then the getTotalCount() function returns
5122      * the data set size</em>
5123      */
5124     getCount : function(){
5125         return this.data.length || 0;
5126     },
5127
5128     /**
5129      * Gets the total number of records in the dataset as returned by the server.
5130      * <p>
5131      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
5132      * the dataset size</em>
5133      */
5134     getTotalCount : function(){
5135         return this.totalLength || 0;
5136     },
5137
5138     /**
5139      * Returns the sort state of the Store as an object with two properties:
5140      * <pre><code>
5141  field {String} The name of the field by which the Records are sorted
5142  direction {String} The sort order, "ASC" or "DESC"
5143      * </code></pre>
5144      */
5145     getSortState : function(){
5146         return this.sortInfo;
5147     },
5148
5149     // private
5150     applySort : function(){
5151         if(this.sortInfo && !this.remoteSort){
5152             var s = this.sortInfo, f = s.field;
5153             var st = this.fields.get(f).sortType;
5154             var fn = function(r1, r2){
5155                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
5156                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5157             };
5158             this.data.sort(s.direction, fn);
5159             if(this.snapshot && this.snapshot != this.data){
5160                 this.snapshot.sort(s.direction, fn);
5161             }
5162         }
5163     },
5164
5165     /**
5166      * Sets the default sort column and order to be used by the next load operation.
5167      * @param {String} fieldName The name of the field to sort by.
5168      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5169      */
5170     setDefaultSort : function(field, dir){
5171         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
5172     },
5173
5174     /**
5175      * Sort the Records.
5176      * If remote sorting is used, the sort is performed on the server, and the cache is
5177      * reloaded. If local sorting is used, the cache is sorted internally.
5178      * @param {String} fieldName The name of the field to sort by.
5179      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5180      */
5181     sort : function(fieldName, dir){
5182         var f = this.fields.get(fieldName);
5183         if(!dir){
5184             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
5185             
5186             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
5187                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
5188             }else{
5189                 dir = f.sortDir;
5190             }
5191         }
5192         this.sortToggle[f.name] = dir;
5193         this.sortInfo = {field: f.name, direction: dir};
5194         if(!this.remoteSort){
5195             this.applySort();
5196             this.fireEvent("datachanged", this);
5197         }else{
5198             this.load(this.lastOptions);
5199         }
5200     },
5201
5202     /**
5203      * Calls the specified function for each of the Records in the cache.
5204      * @param {Function} fn The function to call. The Record is passed as the first parameter.
5205      * Returning <em>false</em> aborts and exits the iteration.
5206      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
5207      */
5208     each : function(fn, scope){
5209         this.data.each(fn, scope);
5210     },
5211
5212     /**
5213      * Gets all records modified since the last commit.  Modified records are persisted across load operations
5214      * (e.g., during paging).
5215      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
5216      */
5217     getModifiedRecords : function(){
5218         return this.modified;
5219     },
5220
5221     // private
5222     createFilterFn : function(property, value, anyMatch){
5223         if(!value.exec){ // not a regex
5224             value = String(value);
5225             if(value.length == 0){
5226                 return false;
5227             }
5228             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
5229         }
5230         return function(r){
5231             return value.test(r.data[property]);
5232         };
5233     },
5234
5235     /**
5236      * Sums the value of <i>property</i> for each record between start and end and returns the result.
5237      * @param {String} property A field on your records
5238      * @param {Number} start The record index to start at (defaults to 0)
5239      * @param {Number} end The last record index to include (defaults to length - 1)
5240      * @return {Number} The sum
5241      */
5242     sum : function(property, start, end){
5243         var rs = this.data.items, v = 0;
5244         start = start || 0;
5245         end = (end || end === 0) ? end : rs.length-1;
5246
5247         for(var i = start; i <= end; i++){
5248             v += (rs[i].data[property] || 0);
5249         }
5250         return v;
5251     },
5252
5253     /**
5254      * Filter the records by a specified property.
5255      * @param {String} field A field on your records
5256      * @param {String/RegExp} value Either a string that the field
5257      * should start with or a RegExp to test against the field
5258      * @param {Boolean} anyMatch True to match any part not just the beginning
5259      */
5260     filter : function(property, value, anyMatch){
5261         var fn = this.createFilterFn(property, value, anyMatch);
5262         return fn ? this.filterBy(fn) : this.clearFilter();
5263     },
5264
5265     /**
5266      * Filter by a function. The specified function will be called with each
5267      * record in this data source. If the function returns true the record is included,
5268      * otherwise it is filtered.
5269      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5270      * @param {Object} scope (optional) The scope of the function (defaults to this)
5271      */
5272     filterBy : function(fn, scope){
5273         this.snapshot = this.snapshot || this.data;
5274         this.data = this.queryBy(fn, scope||this);
5275         this.fireEvent("datachanged", this);
5276     },
5277
5278     /**
5279      * Query the records by a specified property.
5280      * @param {String} field A field on your records
5281      * @param {String/RegExp} value Either a string that the field
5282      * should start with or a RegExp to test against the field
5283      * @param {Boolean} anyMatch True to match any part not just the beginning
5284      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5285      */
5286     query : function(property, value, anyMatch){
5287         var fn = this.createFilterFn(property, value, anyMatch);
5288         return fn ? this.queryBy(fn) : this.data.clone();
5289     },
5290
5291     /**
5292      * Query by a function. The specified function will be called with each
5293      * record in this data source. If the function returns true the record is included
5294      * in the results.
5295      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5296      * @param {Object} scope (optional) The scope of the function (defaults to this)
5297       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5298      **/
5299     queryBy : function(fn, scope){
5300         var data = this.snapshot || this.data;
5301         return data.filterBy(fn, scope||this);
5302     },
5303
5304     /**
5305      * Collects unique values for a particular dataIndex from this store.
5306      * @param {String} dataIndex The property to collect
5307      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
5308      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
5309      * @return {Array} An array of the unique values
5310      **/
5311     collect : function(dataIndex, allowNull, bypassFilter){
5312         var d = (bypassFilter === true && this.snapshot) ?
5313                 this.snapshot.items : this.data.items;
5314         var v, sv, r = [], l = {};
5315         for(var i = 0, len = d.length; i < len; i++){
5316             v = d[i].data[dataIndex];
5317             sv = String(v);
5318             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
5319                 l[sv] = true;
5320                 r[r.length] = v;
5321             }
5322         }
5323         return r;
5324     },
5325
5326     /**
5327      * Revert to a view of the Record cache with no filtering applied.
5328      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
5329      */
5330     clearFilter : function(suppressEvent){
5331         if(this.snapshot && this.snapshot != this.data){
5332             this.data = this.snapshot;
5333             delete this.snapshot;
5334             if(suppressEvent !== true){
5335                 this.fireEvent("datachanged", this);
5336             }
5337         }
5338     },
5339
5340     // private
5341     afterEdit : function(record){
5342         if(this.modified.indexOf(record) == -1){
5343             this.modified.push(record);
5344         }
5345         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
5346     },
5347     
5348     // private
5349     afterReject : function(record){
5350         this.modified.remove(record);
5351         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
5352     },
5353
5354     // private
5355     afterCommit : function(record){
5356         this.modified.remove(record);
5357         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
5358     },
5359
5360     /**
5361      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
5362      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
5363      */
5364     commitChanges : function(){
5365         var m = this.modified.slice(0);
5366         this.modified = [];
5367         for(var i = 0, len = m.length; i < len; i++){
5368             m[i].commit();
5369         }
5370     },
5371
5372     /**
5373      * Cancel outstanding changes on all changed records.
5374      */
5375     rejectChanges : function(){
5376         var m = this.modified.slice(0);
5377         this.modified = [];
5378         for(var i = 0, len = m.length; i < len; i++){
5379             m[i].reject();
5380         }
5381     },
5382
5383     onMetaChange : function(meta, rtype, o){
5384         this.recordType = rtype;
5385         this.fields = rtype.prototype.fields;
5386         delete this.snapshot;
5387         this.sortInfo = meta.sortInfo || this.sortInfo;
5388         this.modified = [];
5389         this.fireEvent('metachange', this, this.reader.meta);
5390     }
5391 });/*
5392  * Based on:
5393  * Ext JS Library 1.1.1
5394  * Copyright(c) 2006-2007, Ext JS, LLC.
5395  *
5396  * Originally Released Under LGPL - original licence link has changed is not relivant.
5397  *
5398  * Fork - LGPL
5399  * <script type="text/javascript">
5400  */
5401
5402 /**
5403  * @class Roo.data.SimpleStore
5404  * @extends Roo.data.Store
5405  * Small helper class to make creating Stores from Array data easier.
5406  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
5407  * @cfg {Array} fields An array of field definition objects, or field name strings.
5408  * @cfg {Array} data The multi-dimensional array of data
5409  * @constructor
5410  * @param {Object} config
5411  */
5412 Roo.data.SimpleStore = function(config){
5413     Roo.data.SimpleStore.superclass.constructor.call(this, {
5414         isLocal : true,
5415         reader: new Roo.data.ArrayReader({
5416                 id: config.id
5417             },
5418             Roo.data.Record.create(config.fields)
5419         ),
5420         proxy : new Roo.data.MemoryProxy(config.data)
5421     });
5422     this.load();
5423 };
5424 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
5425  * Based on:
5426  * Ext JS Library 1.1.1
5427  * Copyright(c) 2006-2007, Ext JS, LLC.
5428  *
5429  * Originally Released Under LGPL - original licence link has changed is not relivant.
5430  *
5431  * Fork - LGPL
5432  * <script type="text/javascript">
5433  */
5434
5435 /**
5436 /**
5437  * @extends Roo.data.Store
5438  * @class Roo.data.JsonStore
5439  * Small helper class to make creating Stores for JSON data easier. <br/>
5440 <pre><code>
5441 var store = new Roo.data.JsonStore({
5442     url: 'get-images.php',
5443     root: 'images',
5444     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
5445 });
5446 </code></pre>
5447  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
5448  * JsonReader and HttpProxy (unless inline data is provided).</b>
5449  * @cfg {Array} fields An array of field definition objects, or field name strings.
5450  * @constructor
5451  * @param {Object} config
5452  */
5453 Roo.data.JsonStore = function(c){
5454     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
5455         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
5456         reader: new Roo.data.JsonReader(c, c.fields)
5457     }));
5458 };
5459 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
5460  * Based on:
5461  * Ext JS Library 1.1.1
5462  * Copyright(c) 2006-2007, Ext JS, LLC.
5463  *
5464  * Originally Released Under LGPL - original licence link has changed is not relivant.
5465  *
5466  * Fork - LGPL
5467  * <script type="text/javascript">
5468  */
5469
5470  
5471 Roo.data.Field = function(config){
5472     if(typeof config == "string"){
5473         config = {name: config};
5474     }
5475     Roo.apply(this, config);
5476     
5477     if(!this.type){
5478         this.type = "auto";
5479     }
5480     
5481     var st = Roo.data.SortTypes;
5482     // named sortTypes are supported, here we look them up
5483     if(typeof this.sortType == "string"){
5484         this.sortType = st[this.sortType];
5485     }
5486     
5487     // set default sortType for strings and dates
5488     if(!this.sortType){
5489         switch(this.type){
5490             case "string":
5491                 this.sortType = st.asUCString;
5492                 break;
5493             case "date":
5494                 this.sortType = st.asDate;
5495                 break;
5496             default:
5497                 this.sortType = st.none;
5498         }
5499     }
5500
5501     // define once
5502     var stripRe = /[\$,%]/g;
5503
5504     // prebuilt conversion function for this field, instead of
5505     // switching every time we're reading a value
5506     if(!this.convert){
5507         var cv, dateFormat = this.dateFormat;
5508         switch(this.type){
5509             case "":
5510             case "auto":
5511             case undefined:
5512                 cv = function(v){ return v; };
5513                 break;
5514             case "string":
5515                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
5516                 break;
5517             case "int":
5518                 cv = function(v){
5519                     return v !== undefined && v !== null && v !== '' ?
5520                            parseInt(String(v).replace(stripRe, ""), 10) : '';
5521                     };
5522                 break;
5523             case "float":
5524                 cv = function(v){
5525                     return v !== undefined && v !== null && v !== '' ?
5526                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
5527                     };
5528                 break;
5529             case "bool":
5530             case "boolean":
5531                 cv = function(v){ return v === true || v === "true" || v == 1; };
5532                 break;
5533             case "date":
5534                 cv = function(v){
5535                     if(!v){
5536                         return '';
5537                     }
5538                     if(v instanceof Date){
5539                         return v;
5540                     }
5541                     if(dateFormat){
5542                         if(dateFormat == "timestamp"){
5543                             return new Date(v*1000);
5544                         }
5545                         return Date.parseDate(v, dateFormat);
5546                     }
5547                     var parsed = Date.parse(v);
5548                     return parsed ? new Date(parsed) : null;
5549                 };
5550              break;
5551             
5552         }
5553         this.convert = cv;
5554     }
5555 };
5556
5557 Roo.data.Field.prototype = {
5558     dateFormat: null,
5559     defaultValue: "",
5560     mapping: null,
5561     sortType : null,
5562     sortDir : "ASC"
5563 };/*
5564  * Based on:
5565  * Ext JS Library 1.1.1
5566  * Copyright(c) 2006-2007, Ext JS, LLC.
5567  *
5568  * Originally Released Under LGPL - original licence link has changed is not relivant.
5569  *
5570  * Fork - LGPL
5571  * <script type="text/javascript">
5572  */
5573  
5574 // Base class for reading structured data from a data source.  This class is intended to be
5575 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
5576
5577 /**
5578  * @class Roo.data.DataReader
5579  * Base class for reading structured data from a data source.  This class is intended to be
5580  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
5581  */
5582
5583 Roo.data.DataReader = function(meta, recordType){
5584     
5585     this.meta = meta;
5586     
5587     this.recordType = recordType instanceof Array ? 
5588         Roo.data.Record.create(recordType) : recordType;
5589 };
5590
5591 Roo.data.DataReader.prototype = {
5592      /**
5593      * Create an empty record
5594      * @param {Object} data (optional) - overlay some values
5595      * @return {Roo.data.Record} record created.
5596      */
5597     newRow :  function(d) {
5598         var da =  {};
5599         this.recordType.prototype.fields.each(function(c) {
5600             switch( c.type) {
5601                 case 'int' : da[c.name] = 0; break;
5602                 case 'date' : da[c.name] = new Date(); break;
5603                 case 'float' : da[c.name] = 0.0; break;
5604                 case 'boolean' : da[c.name] = false; break;
5605                 default : da[c.name] = ""; break;
5606             }
5607             
5608         });
5609         return new this.recordType(Roo.apply(da, d));
5610     }
5611     
5612 };/*
5613  * Based on:
5614  * Ext JS Library 1.1.1
5615  * Copyright(c) 2006-2007, Ext JS, LLC.
5616  *
5617  * Originally Released Under LGPL - original licence link has changed is not relivant.
5618  *
5619  * Fork - LGPL
5620  * <script type="text/javascript">
5621  */
5622
5623 /**
5624  * @class Roo.data.DataProxy
5625  * @extends Roo.data.Observable
5626  * This class is an abstract base class for implementations which provide retrieval of
5627  * unformatted data objects.<br>
5628  * <p>
5629  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
5630  * (of the appropriate type which knows how to parse the data object) to provide a block of
5631  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
5632  * <p>
5633  * Custom implementations must implement the load method as described in
5634  * {@link Roo.data.HttpProxy#load}.
5635  */
5636 Roo.data.DataProxy = function(){
5637     this.addEvents({
5638         /**
5639          * @event beforeload
5640          * Fires before a network request is made to retrieve a data object.
5641          * @param {Object} This DataProxy object.
5642          * @param {Object} params The params parameter to the load function.
5643          */
5644         beforeload : true,
5645         /**
5646          * @event load
5647          * Fires before the load method's callback is called.
5648          * @param {Object} This DataProxy object.
5649          * @param {Object} o The data object.
5650          * @param {Object} arg The callback argument object passed to the load function.
5651          */
5652         load : true,
5653         /**
5654          * @event loadexception
5655          * Fires if an Exception occurs during data retrieval.
5656          * @param {Object} This DataProxy object.
5657          * @param {Object} o The data object.
5658          * @param {Object} arg The callback argument object passed to the load function.
5659          * @param {Object} e The Exception.
5660          */
5661         loadexception : true
5662     });
5663     Roo.data.DataProxy.superclass.constructor.call(this);
5664 };
5665
5666 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
5667
5668     /**
5669      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
5670      */
5671 /*
5672  * Based on:
5673  * Ext JS Library 1.1.1
5674  * Copyright(c) 2006-2007, Ext JS, LLC.
5675  *
5676  * Originally Released Under LGPL - original licence link has changed is not relivant.
5677  *
5678  * Fork - LGPL
5679  * <script type="text/javascript">
5680  */
5681 /**
5682  * @class Roo.data.MemoryProxy
5683  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
5684  * to the Reader when its load method is called.
5685  * @constructor
5686  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
5687  */
5688 Roo.data.MemoryProxy = function(data){
5689     if (data.data) {
5690         data = data.data;
5691     }
5692     Roo.data.MemoryProxy.superclass.constructor.call(this);
5693     this.data = data;
5694 };
5695
5696 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
5697     /**
5698      * Load data from the requested source (in this case an in-memory
5699      * data object passed to the constructor), read the data object into
5700      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5701      * process that block using the passed callback.
5702      * @param {Object} params This parameter is not used by the MemoryProxy class.
5703      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5704      * object into a block of Roo.data.Records.
5705      * @param {Function} callback The function into which to pass the block of Roo.data.records.
5706      * The function must be passed <ul>
5707      * <li>The Record block object</li>
5708      * <li>The "arg" argument from the load function</li>
5709      * <li>A boolean success indicator</li>
5710      * </ul>
5711      * @param {Object} scope The scope in which to call the callback
5712      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5713      */
5714     load : function(params, reader, callback, scope, arg){
5715         params = params || {};
5716         var result;
5717         try {
5718             result = reader.readRecords(this.data);
5719         }catch(e){
5720             this.fireEvent("loadexception", this, arg, null, e);
5721             callback.call(scope, null, arg, false);
5722             return;
5723         }
5724         callback.call(scope, result, arg, true);
5725     },
5726     
5727     // private
5728     update : function(params, records){
5729         
5730     }
5731 });/*
5732  * Based on:
5733  * Ext JS Library 1.1.1
5734  * Copyright(c) 2006-2007, Ext JS, LLC.
5735  *
5736  * Originally Released Under LGPL - original licence link has changed is not relivant.
5737  *
5738  * Fork - LGPL
5739  * <script type="text/javascript">
5740  */
5741 /**
5742  * @class Roo.data.HttpProxy
5743  * @extends Roo.data.DataProxy
5744  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
5745  * configured to reference a certain URL.<br><br>
5746  * <p>
5747  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
5748  * from which the running page was served.<br><br>
5749  * <p>
5750  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
5751  * <p>
5752  * Be aware that to enable the browser to parse an XML document, the server must set
5753  * the Content-Type header in the HTTP response to "text/xml".
5754  * @constructor
5755  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
5756  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
5757  * will be used to make the request.
5758  */
5759 Roo.data.HttpProxy = function(conn){
5760     Roo.data.HttpProxy.superclass.constructor.call(this);
5761     // is conn a conn config or a real conn?
5762     this.conn = conn;
5763     this.useAjax = !conn || !conn.events;
5764   
5765 };
5766
5767 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
5768     // thse are take from connection...
5769     
5770     /**
5771      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
5772      */
5773     /**
5774      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
5775      * extra parameters to each request made by this object. (defaults to undefined)
5776      */
5777     /**
5778      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
5779      *  to each request made by this object. (defaults to undefined)
5780      */
5781     /**
5782      * @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)
5783      */
5784     /**
5785      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
5786      */
5787      /**
5788      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
5789      * @type Boolean
5790      */
5791   
5792
5793     /**
5794      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
5795      * @type Boolean
5796      */
5797     /**
5798      * Return the {@link Roo.data.Connection} object being used by this Proxy.
5799      * @return {Connection} The Connection object. This object may be used to subscribe to events on
5800      * a finer-grained basis than the DataProxy events.
5801      */
5802     getConnection : function(){
5803         return this.useAjax ? Roo.Ajax : this.conn;
5804     },
5805
5806     /**
5807      * Load data from the configured {@link Roo.data.Connection}, read the data object into
5808      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
5809      * process that block using the passed callback.
5810      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5811      * for the request to the remote server.
5812      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5813      * object into a block of Roo.data.Records.
5814      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5815      * The function must be passed <ul>
5816      * <li>The Record block object</li>
5817      * <li>The "arg" argument from the load function</li>
5818      * <li>A boolean success indicator</li>
5819      * </ul>
5820      * @param {Object} scope The scope in which to call the callback
5821      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5822      */
5823     load : function(params, reader, callback, scope, arg){
5824         if(this.fireEvent("beforeload", this, params) !== false){
5825             var  o = {
5826                 params : params || {},
5827                 request: {
5828                     callback : callback,
5829                     scope : scope,
5830                     arg : arg
5831                 },
5832                 reader: reader,
5833                 callback : this.loadResponse,
5834                 scope: this
5835             };
5836             if(this.useAjax){
5837                 Roo.applyIf(o, this.conn);
5838                 if(this.activeRequest){
5839                     Roo.Ajax.abort(this.activeRequest);
5840                 }
5841                 this.activeRequest = Roo.Ajax.request(o);
5842             }else{
5843                 this.conn.request(o);
5844             }
5845         }else{
5846             callback.call(scope||this, null, arg, false);
5847         }
5848     },
5849
5850     // private
5851     loadResponse : function(o, success, response){
5852         delete this.activeRequest;
5853         if(!success){
5854             this.fireEvent("loadexception", this, o, response);
5855             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5856             return;
5857         }
5858         var result;
5859         try {
5860             result = o.reader.read(response);
5861         }catch(e){
5862             this.fireEvent("loadexception", this, o, response, e);
5863             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5864             return;
5865         }
5866         
5867         this.fireEvent("load", this, o, o.request.arg);
5868         o.request.callback.call(o.request.scope, result, o.request.arg, true);
5869     },
5870
5871     // private
5872     update : function(dataSet){
5873
5874     },
5875
5876     // private
5877     updateResponse : function(dataSet){
5878
5879     }
5880 });/*
5881  * Based on:
5882  * Ext JS Library 1.1.1
5883  * Copyright(c) 2006-2007, Ext JS, LLC.
5884  *
5885  * Originally Released Under LGPL - original licence link has changed is not relivant.
5886  *
5887  * Fork - LGPL
5888  * <script type="text/javascript">
5889  */
5890
5891 /**
5892  * @class Roo.data.ScriptTagProxy
5893  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
5894  * other than the originating domain of the running page.<br><br>
5895  * <p>
5896  * <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
5897  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
5898  * <p>
5899  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
5900  * source code that is used as the source inside a &lt;script> tag.<br><br>
5901  * <p>
5902  * In order for the browser to process the returned data, the server must wrap the data object
5903  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
5904  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
5905  * depending on whether the callback name was passed:
5906  * <p>
5907  * <pre><code>
5908 boolean scriptTag = false;
5909 String cb = request.getParameter("callback");
5910 if (cb != null) {
5911     scriptTag = true;
5912     response.setContentType("text/javascript");
5913 } else {
5914     response.setContentType("application/x-json");
5915 }
5916 Writer out = response.getWriter();
5917 if (scriptTag) {
5918     out.write(cb + "(");
5919 }
5920 out.print(dataBlock.toJsonString());
5921 if (scriptTag) {
5922     out.write(");");
5923 }
5924 </pre></code>
5925  *
5926  * @constructor
5927  * @param {Object} config A configuration object.
5928  */
5929 Roo.data.ScriptTagProxy = function(config){
5930     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
5931     Roo.apply(this, config);
5932     this.head = document.getElementsByTagName("head")[0];
5933 };
5934
5935 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
5936
5937 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
5938     /**
5939      * @cfg {String} url The URL from which to request the data object.
5940      */
5941     /**
5942      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
5943      */
5944     timeout : 30000,
5945     /**
5946      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
5947      * the server the name of the callback function set up by the load call to process the returned data object.
5948      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
5949      * javascript output which calls this named function passing the data object as its only parameter.
5950      */
5951     callbackParam : "callback",
5952     /**
5953      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
5954      * name to the request.
5955      */
5956     nocache : true,
5957
5958     /**
5959      * Load data from the configured URL, read the data object into
5960      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5961      * process that block using the passed callback.
5962      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5963      * for the request to the remote server.
5964      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5965      * object into a block of Roo.data.Records.
5966      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5967      * The function must be passed <ul>
5968      * <li>The Record block object</li>
5969      * <li>The "arg" argument from the load function</li>
5970      * <li>A boolean success indicator</li>
5971      * </ul>
5972      * @param {Object} scope The scope in which to call the callback
5973      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5974      */
5975     load : function(params, reader, callback, scope, arg){
5976         if(this.fireEvent("beforeload", this, params) !== false){
5977
5978             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
5979
5980             var url = this.url;
5981             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
5982             if(this.nocache){
5983                 url += "&_dc=" + (new Date().getTime());
5984             }
5985             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
5986             var trans = {
5987                 id : transId,
5988                 cb : "stcCallback"+transId,
5989                 scriptId : "stcScript"+transId,
5990                 params : params,
5991                 arg : arg,
5992                 url : url,
5993                 callback : callback,
5994                 scope : scope,
5995                 reader : reader
5996             };
5997             var conn = this;
5998
5999             window[trans.cb] = function(o){
6000                 conn.handleResponse(o, trans);
6001             };
6002
6003             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
6004
6005             if(this.autoAbort !== false){
6006                 this.abort();
6007             }
6008
6009             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
6010
6011             var script = document.createElement("script");
6012             script.setAttribute("src", url);
6013             script.setAttribute("type", "text/javascript");
6014             script.setAttribute("id", trans.scriptId);
6015             this.head.appendChild(script);
6016
6017             this.trans = trans;
6018         }else{
6019             callback.call(scope||this, null, arg, false);
6020         }
6021     },
6022
6023     // private
6024     isLoading : function(){
6025         return this.trans ? true : false;
6026     },
6027
6028     /**
6029      * Abort the current server request.
6030      */
6031     abort : function(){
6032         if(this.isLoading()){
6033             this.destroyTrans(this.trans);
6034         }
6035     },
6036
6037     // private
6038     destroyTrans : function(trans, isLoaded){
6039         this.head.removeChild(document.getElementById(trans.scriptId));
6040         clearTimeout(trans.timeoutId);
6041         if(isLoaded){
6042             window[trans.cb] = undefined;
6043             try{
6044                 delete window[trans.cb];
6045             }catch(e){}
6046         }else{
6047             // if hasn't been loaded, wait for load to remove it to prevent script error
6048             window[trans.cb] = function(){
6049                 window[trans.cb] = undefined;
6050                 try{
6051                     delete window[trans.cb];
6052                 }catch(e){}
6053             };
6054         }
6055     },
6056
6057     // private
6058     handleResponse : function(o, trans){
6059         this.trans = false;
6060         this.destroyTrans(trans, true);
6061         var result;
6062         try {
6063             result = trans.reader.readRecords(o);
6064         }catch(e){
6065             this.fireEvent("loadexception", this, o, trans.arg, e);
6066             trans.callback.call(trans.scope||window, null, trans.arg, false);
6067             return;
6068         }
6069         this.fireEvent("load", this, o, trans.arg);
6070         trans.callback.call(trans.scope||window, result, trans.arg, true);
6071     },
6072
6073     // private
6074     handleFailure : function(trans){
6075         this.trans = false;
6076         this.destroyTrans(trans, false);
6077         this.fireEvent("loadexception", this, null, trans.arg);
6078         trans.callback.call(trans.scope||window, null, trans.arg, false);
6079     }
6080 });/*
6081  * Based on:
6082  * Ext JS Library 1.1.1
6083  * Copyright(c) 2006-2007, Ext JS, LLC.
6084  *
6085  * Originally Released Under LGPL - original licence link has changed is not relivant.
6086  *
6087  * Fork - LGPL
6088  * <script type="text/javascript">
6089  */
6090
6091 /**
6092  * @class Roo.data.JsonReader
6093  * @extends Roo.data.DataReader
6094  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
6095  * based on mappings in a provided Roo.data.Record constructor.
6096  * 
6097  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
6098  * in the reply previously. 
6099  * 
6100  * <p>
6101  * Example code:
6102  * <pre><code>
6103 var RecordDef = Roo.data.Record.create([
6104     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6105     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6106 ]);
6107 var myReader = new Roo.data.JsonReader({
6108     totalProperty: "results",    // The property which contains the total dataset size (optional)
6109     root: "rows",                // The property which contains an Array of row objects
6110     id: "id"                     // The property within each row object that provides an ID for the record (optional)
6111 }, RecordDef);
6112 </code></pre>
6113  * <p>
6114  * This would consume a JSON file like this:
6115  * <pre><code>
6116 { 'results': 2, 'rows': [
6117     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
6118     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
6119 }
6120 </code></pre>
6121  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
6122  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6123  * paged from the remote server.
6124  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
6125  * @cfg {String} root name of the property which contains the Array of row objects.
6126  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
6127  * @constructor
6128  * Create a new JsonReader
6129  * @param {Object} meta Metadata configuration options
6130  * @param {Object} recordType Either an Array of field definition objects,
6131  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
6132  */
6133 Roo.data.JsonReader = function(meta, recordType){
6134     
6135     meta = meta || {};
6136     // set some defaults:
6137     Roo.applyIf(meta, {
6138         totalProperty: 'total',
6139         successProperty : 'success',
6140         root : 'data',
6141         id : 'id'
6142     });
6143     
6144     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6145 };
6146 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
6147     
6148     /**
6149      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
6150      * Used by Store query builder to append _requestMeta to params.
6151      * 
6152      */
6153     metaFromRemote : false,
6154     /**
6155      * This method is only used by a DataProxy which has retrieved data from a remote server.
6156      * @param {Object} response The XHR object which contains the JSON data in its responseText.
6157      * @return {Object} data A data block which is used by an Roo.data.Store object as
6158      * a cache of Roo.data.Records.
6159      */
6160     read : function(response){
6161         var json = response.responseText;
6162        
6163         var o = /* eval:var:o */ eval("("+json+")");
6164         if(!o) {
6165             throw {message: "JsonReader.read: Json object not found"};
6166         }
6167         
6168         if(o.metaData){
6169             
6170             delete this.ef;
6171             this.metaFromRemote = true;
6172             this.meta = o.metaData;
6173             this.recordType = Roo.data.Record.create(o.metaData.fields);
6174             this.onMetaChange(this.meta, this.recordType, o);
6175         }
6176         return this.readRecords(o);
6177     },
6178
6179     // private function a store will implement
6180     onMetaChange : function(meta, recordType, o){
6181
6182     },
6183
6184     /**
6185          * @ignore
6186          */
6187     simpleAccess: function(obj, subsc) {
6188         return obj[subsc];
6189     },
6190
6191         /**
6192          * @ignore
6193          */
6194     getJsonAccessor: function(){
6195         var re = /[\[\.]/;
6196         return function(expr) {
6197             try {
6198                 return(re.test(expr))
6199                     ? new Function("obj", "return obj." + expr)
6200                     : function(obj){
6201                         return obj[expr];
6202                     };
6203             } catch(e){}
6204             return Roo.emptyFn;
6205         };
6206     }(),
6207
6208     /**
6209      * Create a data block containing Roo.data.Records from an XML document.
6210      * @param {Object} o An object which contains an Array of row objects in the property specified
6211      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
6212      * which contains the total size of the dataset.
6213      * @return {Object} data A data block which is used by an Roo.data.Store object as
6214      * a cache of Roo.data.Records.
6215      */
6216     readRecords : function(o){
6217         /**
6218          * After any data loads, the raw JSON data is available for further custom processing.
6219          * @type Object
6220          */
6221         this.jsonData = o;
6222         var s = this.meta, Record = this.recordType,
6223             f = Record.prototype.fields, fi = f.items, fl = f.length;
6224
6225 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
6226         if (!this.ef) {
6227             if(s.totalProperty) {
6228                     this.getTotal = this.getJsonAccessor(s.totalProperty);
6229                 }
6230                 if(s.successProperty) {
6231                     this.getSuccess = this.getJsonAccessor(s.successProperty);
6232                 }
6233                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
6234                 if (s.id) {
6235                         var g = this.getJsonAccessor(s.id);
6236                         this.getId = function(rec) {
6237                                 var r = g(rec);
6238                                 return (r === undefined || r === "") ? null : r;
6239                         };
6240                 } else {
6241                         this.getId = function(){return null;};
6242                 }
6243             this.ef = [];
6244             for(var jj = 0; jj < fl; jj++){
6245                 f = fi[jj];
6246                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
6247                 this.ef[jj] = this.getJsonAccessor(map);
6248             }
6249         }
6250
6251         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
6252         if(s.totalProperty){
6253             var vt = parseInt(this.getTotal(o), 10);
6254             if(!isNaN(vt)){
6255                 totalRecords = vt;
6256             }
6257         }
6258         if(s.successProperty){
6259             var vs = this.getSuccess(o);
6260             if(vs === false || vs === 'false'){
6261                 success = false;
6262             }
6263         }
6264         var records = [];
6265             for(var i = 0; i < c; i++){
6266                     var n = root[i];
6267                 var values = {};
6268                 var id = this.getId(n);
6269                 for(var j = 0; j < fl; j++){
6270                     f = fi[j];
6271                 var v = this.ef[j](n);
6272                 if (!f.convert) {
6273                     Roo.log('missing convert for ' + f.name);
6274                     Roo.log(f);
6275                     continue;
6276                 }
6277                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
6278                 }
6279                 var record = new Record(values, id);
6280                 record.json = n;
6281                 records[i] = record;
6282             }
6283             return {
6284                 success : success,
6285                 records : records,
6286                 totalRecords : totalRecords
6287             };
6288     }
6289 });/*
6290  * Based on:
6291  * Ext JS Library 1.1.1
6292  * Copyright(c) 2006-2007, Ext JS, LLC.
6293  *
6294  * Originally Released Under LGPL - original licence link has changed is not relivant.
6295  *
6296  * Fork - LGPL
6297  * <script type="text/javascript">
6298  */
6299
6300 /**
6301  * @class Roo.data.XmlReader
6302  * @extends Roo.data.DataReader
6303  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
6304  * based on mappings in a provided Roo.data.Record constructor.<br><br>
6305  * <p>
6306  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
6307  * header in the HTTP response must be set to "text/xml".</em>
6308  * <p>
6309  * Example code:
6310  * <pre><code>
6311 var RecordDef = Roo.data.Record.create([
6312    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6313    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6314 ]);
6315 var myReader = new Roo.data.XmlReader({
6316    totalRecords: "results", // The element which contains the total dataset size (optional)
6317    record: "row",           // The repeated element which contains row information
6318    id: "id"                 // The element within the row that provides an ID for the record (optional)
6319 }, RecordDef);
6320 </code></pre>
6321  * <p>
6322  * This would consume an XML file like this:
6323  * <pre><code>
6324 &lt;?xml?>
6325 &lt;dataset>
6326  &lt;results>2&lt;/results>
6327  &lt;row>
6328    &lt;id>1&lt;/id>
6329    &lt;name>Bill&lt;/name>
6330    &lt;occupation>Gardener&lt;/occupation>
6331  &lt;/row>
6332  &lt;row>
6333    &lt;id>2&lt;/id>
6334    &lt;name>Ben&lt;/name>
6335    &lt;occupation>Horticulturalist&lt;/occupation>
6336  &lt;/row>
6337 &lt;/dataset>
6338 </code></pre>
6339  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
6340  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6341  * paged from the remote server.
6342  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
6343  * @cfg {String} success The DomQuery path to the success attribute used by forms.
6344  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
6345  * a record identifier value.
6346  * @constructor
6347  * Create a new XmlReader
6348  * @param {Object} meta Metadata configuration options
6349  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
6350  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
6351  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
6352  */
6353 Roo.data.XmlReader = function(meta, recordType){
6354     meta = meta || {};
6355     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6356 };
6357 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
6358     /**
6359      * This method is only used by a DataProxy which has retrieved data from a remote server.
6360          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
6361          * to contain a method called 'responseXML' that returns an XML document object.
6362      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6363      * a cache of Roo.data.Records.
6364      */
6365     read : function(response){
6366         var doc = response.responseXML;
6367         if(!doc) {
6368             throw {message: "XmlReader.read: XML Document not available"};
6369         }
6370         return this.readRecords(doc);
6371     },
6372
6373     /**
6374      * Create a data block containing Roo.data.Records from an XML document.
6375          * @param {Object} doc A parsed XML document.
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     readRecords : function(doc){
6380         /**
6381          * After any data loads/reads, the raw XML Document is available for further custom processing.
6382          * @type XMLDocument
6383          */
6384         this.xmlData = doc;
6385         var root = doc.documentElement || doc;
6386         var q = Roo.DomQuery;
6387         var recordType = this.recordType, fields = recordType.prototype.fields;
6388         var sid = this.meta.id;
6389         var totalRecords = 0, success = true;
6390         if(this.meta.totalRecords){
6391             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
6392         }
6393         
6394         if(this.meta.success){
6395             var sv = q.selectValue(this.meta.success, root, true);
6396             success = sv !== false && sv !== 'false';
6397         }
6398         var records = [];
6399         var ns = q.select(this.meta.record, root);
6400         for(var i = 0, len = ns.length; i < len; i++) {
6401                 var n = ns[i];
6402                 var values = {};
6403                 var id = sid ? q.selectValue(sid, n) : undefined;
6404                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6405                     var f = fields.items[j];
6406                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
6407                     v = f.convert(v);
6408                     values[f.name] = v;
6409                 }
6410                 var record = new recordType(values, id);
6411                 record.node = n;
6412                 records[records.length] = record;
6413             }
6414
6415             return {
6416                 success : success,
6417                 records : records,
6418                 totalRecords : totalRecords || records.length
6419             };
6420     }
6421 });/*
6422  * Based on:
6423  * Ext JS Library 1.1.1
6424  * Copyright(c) 2006-2007, Ext JS, LLC.
6425  *
6426  * Originally Released Under LGPL - original licence link has changed is not relivant.
6427  *
6428  * Fork - LGPL
6429  * <script type="text/javascript">
6430  */
6431
6432 /**
6433  * @class Roo.data.ArrayReader
6434  * @extends Roo.data.DataReader
6435  * Data reader class to create an Array of Roo.data.Record objects from an Array.
6436  * Each element of that Array represents a row of data fields. The
6437  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
6438  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
6439  * <p>
6440  * Example code:.
6441  * <pre><code>
6442 var RecordDef = Roo.data.Record.create([
6443     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
6444     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
6445 ]);
6446 var myReader = new Roo.data.ArrayReader({
6447     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
6448 }, RecordDef);
6449 </code></pre>
6450  * <p>
6451  * This would consume an Array like this:
6452  * <pre><code>
6453 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
6454   </code></pre>
6455  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
6456  * @constructor
6457  * Create a new JsonReader
6458  * @param {Object} meta Metadata configuration options.
6459  * @param {Object} recordType Either an Array of field definition objects
6460  * as specified to {@link Roo.data.Record#create},
6461  * or an {@link Roo.data.Record} object
6462  * created using {@link Roo.data.Record#create}.
6463  */
6464 Roo.data.ArrayReader = function(meta, recordType){
6465     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
6466 };
6467
6468 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
6469     /**
6470      * Create a data block containing Roo.data.Records from an XML document.
6471      * @param {Object} o An Array of row objects which represents the dataset.
6472      * @return {Object} data A data block which is used by an Roo.data.Store object as
6473      * a cache of Roo.data.Records.
6474      */
6475     readRecords : function(o){
6476         var sid = this.meta ? this.meta.id : null;
6477         var recordType = this.recordType, fields = recordType.prototype.fields;
6478         var records = [];
6479         var root = o;
6480             for(var i = 0; i < root.length; i++){
6481                     var n = root[i];
6482                 var values = {};
6483                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
6484                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6485                 var f = fields.items[j];
6486                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
6487                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
6488                 v = f.convert(v);
6489                 values[f.name] = v;
6490             }
6491                 var record = new recordType(values, id);
6492                 record.json = n;
6493                 records[records.length] = record;
6494             }
6495             return {
6496                 records : records,
6497                 totalRecords : records.length
6498             };
6499     }
6500 });/*
6501  * Based on:
6502  * Ext JS Library 1.1.1
6503  * Copyright(c) 2006-2007, Ext JS, LLC.
6504  *
6505  * Originally Released Under LGPL - original licence link has changed is not relivant.
6506  *
6507  * Fork - LGPL
6508  * <script type="text/javascript">
6509  */
6510
6511
6512 /**
6513  * @class Roo.data.Tree
6514  * @extends Roo.util.Observable
6515  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
6516  * in the tree have most standard DOM functionality.
6517  * @constructor
6518  * @param {Node} root (optional) The root node
6519  */
6520 Roo.data.Tree = function(root){
6521    this.nodeHash = {};
6522    /**
6523     * The root node for this tree
6524     * @type Node
6525     */
6526    this.root = null;
6527    if(root){
6528        this.setRootNode(root);
6529    }
6530    this.addEvents({
6531        /**
6532         * @event append
6533         * Fires when a new child node is appended to a node in this tree.
6534         * @param {Tree} tree The owner tree
6535         * @param {Node} parent The parent node
6536         * @param {Node} node The newly appended node
6537         * @param {Number} index The index of the newly appended node
6538         */
6539        "append" : true,
6540        /**
6541         * @event remove
6542         * Fires when a child node is removed from a node in this tree.
6543         * @param {Tree} tree The owner tree
6544         * @param {Node} parent The parent node
6545         * @param {Node} node The child node removed
6546         */
6547        "remove" : true,
6548        /**
6549         * @event move
6550         * Fires when a node is moved to a new location in the tree
6551         * @param {Tree} tree The owner tree
6552         * @param {Node} node The node moved
6553         * @param {Node} oldParent The old parent of this node
6554         * @param {Node} newParent The new parent of this node
6555         * @param {Number} index The index it was moved to
6556         */
6557        "move" : true,
6558        /**
6559         * @event insert
6560         * Fires when a new child node is inserted in a node in this tree.
6561         * @param {Tree} tree The owner tree
6562         * @param {Node} parent The parent node
6563         * @param {Node} node The child node inserted
6564         * @param {Node} refNode The child node the node was inserted before
6565         */
6566        "insert" : true,
6567        /**
6568         * @event beforeappend
6569         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
6570         * @param {Tree} tree The owner tree
6571         * @param {Node} parent The parent node
6572         * @param {Node} node The child node to be appended
6573         */
6574        "beforeappend" : true,
6575        /**
6576         * @event beforeremove
6577         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
6578         * @param {Tree} tree The owner tree
6579         * @param {Node} parent The parent node
6580         * @param {Node} node The child node to be removed
6581         */
6582        "beforeremove" : true,
6583        /**
6584         * @event beforemove
6585         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
6586         * @param {Tree} tree The owner tree
6587         * @param {Node} node The node being moved
6588         * @param {Node} oldParent The parent of the node
6589         * @param {Node} newParent The new parent the node is moving to
6590         * @param {Number} index The index it is being moved to
6591         */
6592        "beforemove" : true,
6593        /**
6594         * @event beforeinsert
6595         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
6596         * @param {Tree} tree The owner tree
6597         * @param {Node} parent The parent node
6598         * @param {Node} node The child node to be inserted
6599         * @param {Node} refNode The child node the node is being inserted before
6600         */
6601        "beforeinsert" : true
6602    });
6603
6604     Roo.data.Tree.superclass.constructor.call(this);
6605 };
6606
6607 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
6608     pathSeparator: "/",
6609
6610     proxyNodeEvent : function(){
6611         return this.fireEvent.apply(this, arguments);
6612     },
6613
6614     /**
6615      * Returns the root node for this tree.
6616      * @return {Node}
6617      */
6618     getRootNode : function(){
6619         return this.root;
6620     },
6621
6622     /**
6623      * Sets the root node for this tree.
6624      * @param {Node} node
6625      * @return {Node}
6626      */
6627     setRootNode : function(node){
6628         this.root = node;
6629         node.ownerTree = this;
6630         node.isRoot = true;
6631         this.registerNode(node);
6632         return node;
6633     },
6634
6635     /**
6636      * Gets a node in this tree by its id.
6637      * @param {String} id
6638      * @return {Node}
6639      */
6640     getNodeById : function(id){
6641         return this.nodeHash[id];
6642     },
6643
6644     registerNode : function(node){
6645         this.nodeHash[node.id] = node;
6646     },
6647
6648     unregisterNode : function(node){
6649         delete this.nodeHash[node.id];
6650     },
6651
6652     toString : function(){
6653         return "[Tree"+(this.id?" "+this.id:"")+"]";
6654     }
6655 });
6656
6657 /**
6658  * @class Roo.data.Node
6659  * @extends Roo.util.Observable
6660  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
6661  * @cfg {String} id The id for this node. If one is not specified, one is generated.
6662  * @constructor
6663  * @param {Object} attributes The attributes/config for the node
6664  */
6665 Roo.data.Node = function(attributes){
6666     /**
6667      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
6668      * @type {Object}
6669      */
6670     this.attributes = attributes || {};
6671     this.leaf = this.attributes.leaf;
6672     /**
6673      * The node id. @type String
6674      */
6675     this.id = this.attributes.id;
6676     if(!this.id){
6677         this.id = Roo.id(null, "ynode-");
6678         this.attributes.id = this.id;
6679     }
6680      
6681     
6682     /**
6683      * All child nodes of this node. @type Array
6684      */
6685     this.childNodes = [];
6686     if(!this.childNodes.indexOf){ // indexOf is a must
6687         this.childNodes.indexOf = function(o){
6688             for(var i = 0, len = this.length; i < len; i++){
6689                 if(this[i] == o) {
6690                     return i;
6691                 }
6692             }
6693             return -1;
6694         };
6695     }
6696     /**
6697      * The parent node for this node. @type Node
6698      */
6699     this.parentNode = null;
6700     /**
6701      * The first direct child node of this node, or null if this node has no child nodes. @type Node
6702      */
6703     this.firstChild = null;
6704     /**
6705      * The last direct child node of this node, or null if this node has no child nodes. @type Node
6706      */
6707     this.lastChild = null;
6708     /**
6709      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
6710      */
6711     this.previousSibling = null;
6712     /**
6713      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
6714      */
6715     this.nextSibling = null;
6716
6717     this.addEvents({
6718        /**
6719         * @event append
6720         * Fires when a new child node is appended
6721         * @param {Tree} tree The owner tree
6722         * @param {Node} this This node
6723         * @param {Node} node The newly appended node
6724         * @param {Number} index The index of the newly appended node
6725         */
6726        "append" : true,
6727        /**
6728         * @event remove
6729         * Fires when a child node is removed
6730         * @param {Tree} tree The owner tree
6731         * @param {Node} this This node
6732         * @param {Node} node The removed node
6733         */
6734        "remove" : true,
6735        /**
6736         * @event move
6737         * Fires when this node is moved to a new location in the tree
6738         * @param {Tree} tree The owner tree
6739         * @param {Node} this This node
6740         * @param {Node} oldParent The old parent of this node
6741         * @param {Node} newParent The new parent of this node
6742         * @param {Number} index The index it was moved to
6743         */
6744        "move" : true,
6745        /**
6746         * @event insert
6747         * Fires when a new child node is inserted.
6748         * @param {Tree} tree The owner tree
6749         * @param {Node} this This node
6750         * @param {Node} node The child node inserted
6751         * @param {Node} refNode The child node the node was inserted before
6752         */
6753        "insert" : true,
6754        /**
6755         * @event beforeappend
6756         * Fires before a new child is appended, return false to cancel the append.
6757         * @param {Tree} tree The owner tree
6758         * @param {Node} this This node
6759         * @param {Node} node The child node to be appended
6760         */
6761        "beforeappend" : true,
6762        /**
6763         * @event beforeremove
6764         * Fires before a child is removed, return false to cancel the remove.
6765         * @param {Tree} tree The owner tree
6766         * @param {Node} this This node
6767         * @param {Node} node The child node to be removed
6768         */
6769        "beforeremove" : true,
6770        /**
6771         * @event beforemove
6772         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
6773         * @param {Tree} tree The owner tree
6774         * @param {Node} this This node
6775         * @param {Node} oldParent The parent of this node
6776         * @param {Node} newParent The new parent this node is moving to
6777         * @param {Number} index The index it is being moved to
6778         */
6779        "beforemove" : true,
6780        /**
6781         * @event beforeinsert
6782         * Fires before a new child is inserted, return false to cancel the insert.
6783         * @param {Tree} tree The owner tree
6784         * @param {Node} this This node
6785         * @param {Node} node The child node to be inserted
6786         * @param {Node} refNode The child node the node is being inserted before
6787         */
6788        "beforeinsert" : true
6789    });
6790     this.listeners = this.attributes.listeners;
6791     Roo.data.Node.superclass.constructor.call(this);
6792 };
6793
6794 Roo.extend(Roo.data.Node, Roo.util.Observable, {
6795     fireEvent : function(evtName){
6796         // first do standard event for this node
6797         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
6798             return false;
6799         }
6800         // then bubble it up to the tree if the event wasn't cancelled
6801         var ot = this.getOwnerTree();
6802         if(ot){
6803             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
6804                 return false;
6805             }
6806         }
6807         return true;
6808     },
6809
6810     /**
6811      * Returns true if this node is a leaf
6812      * @return {Boolean}
6813      */
6814     isLeaf : function(){
6815         return this.leaf === true;
6816     },
6817
6818     // private
6819     setFirstChild : function(node){
6820         this.firstChild = node;
6821     },
6822
6823     //private
6824     setLastChild : function(node){
6825         this.lastChild = node;
6826     },
6827
6828
6829     /**
6830      * Returns true if this node is the last child of its parent
6831      * @return {Boolean}
6832      */
6833     isLast : function(){
6834        return (!this.parentNode ? true : this.parentNode.lastChild == this);
6835     },
6836
6837     /**
6838      * Returns true if this node is the first child of its parent
6839      * @return {Boolean}
6840      */
6841     isFirst : function(){
6842        return (!this.parentNode ? true : this.parentNode.firstChild == this);
6843     },
6844
6845     hasChildNodes : function(){
6846         return !this.isLeaf() && this.childNodes.length > 0;
6847     },
6848
6849     /**
6850      * Insert node(s) as the last child node of this node.
6851      * @param {Node/Array} node The node or Array of nodes to append
6852      * @return {Node} The appended node if single append, or null if an array was passed
6853      */
6854     appendChild : function(node){
6855         var multi = false;
6856         if(node instanceof Array){
6857             multi = node;
6858         }else if(arguments.length > 1){
6859             multi = arguments;
6860         }
6861         // if passed an array or multiple args do them one by one
6862         if(multi){
6863             for(var i = 0, len = multi.length; i < len; i++) {
6864                 this.appendChild(multi[i]);
6865             }
6866         }else{
6867             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
6868                 return false;
6869             }
6870             var index = this.childNodes.length;
6871             var oldParent = node.parentNode;
6872             // it's a move, make sure we move it cleanly
6873             if(oldParent){
6874                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
6875                     return false;
6876                 }
6877                 oldParent.removeChild(node);
6878             }
6879             index = this.childNodes.length;
6880             if(index == 0){
6881                 this.setFirstChild(node);
6882             }
6883             this.childNodes.push(node);
6884             node.parentNode = this;
6885             var ps = this.childNodes[index-1];
6886             if(ps){
6887                 node.previousSibling = ps;
6888                 ps.nextSibling = node;
6889             }else{
6890                 node.previousSibling = null;
6891             }
6892             node.nextSibling = null;
6893             this.setLastChild(node);
6894             node.setOwnerTree(this.getOwnerTree());
6895             this.fireEvent("append", this.ownerTree, this, node, index);
6896             if(oldParent){
6897                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
6898             }
6899             return node;
6900         }
6901     },
6902
6903     /**
6904      * Removes a child node from this node.
6905      * @param {Node} node The node to remove
6906      * @return {Node} The removed node
6907      */
6908     removeChild : function(node){
6909         var index = this.childNodes.indexOf(node);
6910         if(index == -1){
6911             return false;
6912         }
6913         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
6914             return false;
6915         }
6916
6917         // remove it from childNodes collection
6918         this.childNodes.splice(index, 1);
6919
6920         // update siblings
6921         if(node.previousSibling){
6922             node.previousSibling.nextSibling = node.nextSibling;
6923         }
6924         if(node.nextSibling){
6925             node.nextSibling.previousSibling = node.previousSibling;
6926         }
6927
6928         // update child refs
6929         if(this.firstChild == node){
6930             this.setFirstChild(node.nextSibling);
6931         }
6932         if(this.lastChild == node){
6933             this.setLastChild(node.previousSibling);
6934         }
6935
6936         node.setOwnerTree(null);
6937         // clear any references from the node
6938         node.parentNode = null;
6939         node.previousSibling = null;
6940         node.nextSibling = null;
6941         this.fireEvent("remove", this.ownerTree, this, node);
6942         return node;
6943     },
6944
6945     /**
6946      * Inserts the first node before the second node in this nodes childNodes collection.
6947      * @param {Node} node The node to insert
6948      * @param {Node} refNode The node to insert before (if null the node is appended)
6949      * @return {Node} The inserted node
6950      */
6951     insertBefore : function(node, refNode){
6952         if(!refNode){ // like standard Dom, refNode can be null for append
6953             return this.appendChild(node);
6954         }
6955         // nothing to do
6956         if(node == refNode){
6957             return false;
6958         }
6959
6960         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
6961             return false;
6962         }
6963         var index = this.childNodes.indexOf(refNode);
6964         var oldParent = node.parentNode;
6965         var refIndex = index;
6966
6967         // when moving internally, indexes will change after remove
6968         if(oldParent == this && this.childNodes.indexOf(node) < index){
6969             refIndex--;
6970         }
6971
6972         // it's a move, make sure we move it cleanly
6973         if(oldParent){
6974             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
6975                 return false;
6976             }
6977             oldParent.removeChild(node);
6978         }
6979         if(refIndex == 0){
6980             this.setFirstChild(node);
6981         }
6982         this.childNodes.splice(refIndex, 0, node);
6983         node.parentNode = this;
6984         var ps = this.childNodes[refIndex-1];
6985         if(ps){
6986             node.previousSibling = ps;
6987             ps.nextSibling = node;
6988         }else{
6989             node.previousSibling = null;
6990         }
6991         node.nextSibling = refNode;
6992         refNode.previousSibling = node;
6993         node.setOwnerTree(this.getOwnerTree());
6994         this.fireEvent("insert", this.ownerTree, this, node, refNode);
6995         if(oldParent){
6996             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
6997         }
6998         return node;
6999     },
7000
7001     /**
7002      * Returns the child node at the specified index.
7003      * @param {Number} index
7004      * @return {Node}
7005      */
7006     item : function(index){
7007         return this.childNodes[index];
7008     },
7009
7010     /**
7011      * Replaces one child node in this node with another.
7012      * @param {Node} newChild The replacement node
7013      * @param {Node} oldChild The node to replace
7014      * @return {Node} The replaced node
7015      */
7016     replaceChild : function(newChild, oldChild){
7017         this.insertBefore(newChild, oldChild);
7018         this.removeChild(oldChild);
7019         return oldChild;
7020     },
7021
7022     /**
7023      * Returns the index of a child node
7024      * @param {Node} node
7025      * @return {Number} The index of the node or -1 if it was not found
7026      */
7027     indexOf : function(child){
7028         return this.childNodes.indexOf(child);
7029     },
7030
7031     /**
7032      * Returns the tree this node is in.
7033      * @return {Tree}
7034      */
7035     getOwnerTree : function(){
7036         // if it doesn't have one, look for one
7037         if(!this.ownerTree){
7038             var p = this;
7039             while(p){
7040                 if(p.ownerTree){
7041                     this.ownerTree = p.ownerTree;
7042                     break;
7043                 }
7044                 p = p.parentNode;
7045             }
7046         }
7047         return this.ownerTree;
7048     },
7049
7050     /**
7051      * Returns depth of this node (the root node has a depth of 0)
7052      * @return {Number}
7053      */
7054     getDepth : function(){
7055         var depth = 0;
7056         var p = this;
7057         while(p.parentNode){
7058             ++depth;
7059             p = p.parentNode;
7060         }
7061         return depth;
7062     },
7063
7064     // private
7065     setOwnerTree : function(tree){
7066         // if it's move, we need to update everyone
7067         if(tree != this.ownerTree){
7068             if(this.ownerTree){
7069                 this.ownerTree.unregisterNode(this);
7070             }
7071             this.ownerTree = tree;
7072             var cs = this.childNodes;
7073             for(var i = 0, len = cs.length; i < len; i++) {
7074                 cs[i].setOwnerTree(tree);
7075             }
7076             if(tree){
7077                 tree.registerNode(this);
7078             }
7079         }
7080     },
7081
7082     /**
7083      * Returns the path for this node. The path can be used to expand or select this node programmatically.
7084      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
7085      * @return {String} The path
7086      */
7087     getPath : function(attr){
7088         attr = attr || "id";
7089         var p = this.parentNode;
7090         var b = [this.attributes[attr]];
7091         while(p){
7092             b.unshift(p.attributes[attr]);
7093             p = p.parentNode;
7094         }
7095         var sep = this.getOwnerTree().pathSeparator;
7096         return sep + b.join(sep);
7097     },
7098
7099     /**
7100      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7101      * function call will be the scope provided or the current node. The arguments to the function
7102      * will be the args provided or the current node. If the function returns false at any point,
7103      * the bubble is stopped.
7104      * @param {Function} fn The function to call
7105      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7106      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7107      */
7108     bubble : function(fn, scope, args){
7109         var p = this;
7110         while(p){
7111             if(fn.call(scope || p, args || p) === false){
7112                 break;
7113             }
7114             p = p.parentNode;
7115         }
7116     },
7117
7118     /**
7119      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7120      * function call will be the scope provided or the current node. The arguments to the function
7121      * will be the args provided or the current node. If the function returns false at any point,
7122      * the cascade is stopped on that branch.
7123      * @param {Function} fn The function to call
7124      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7125      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7126      */
7127     cascade : function(fn, scope, args){
7128         if(fn.call(scope || this, args || this) !== false){
7129             var cs = this.childNodes;
7130             for(var i = 0, len = cs.length; i < len; i++) {
7131                 cs[i].cascade(fn, scope, args);
7132             }
7133         }
7134     },
7135
7136     /**
7137      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
7138      * function call will be the scope provided or the current node. The arguments to the function
7139      * will be the args provided or the current node. If the function returns false at any point,
7140      * the iteration stops.
7141      * @param {Function} fn The function to call
7142      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7143      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7144      */
7145     eachChild : function(fn, scope, args){
7146         var cs = this.childNodes;
7147         for(var i = 0, len = cs.length; i < len; i++) {
7148                 if(fn.call(scope || this, args || cs[i]) === false){
7149                     break;
7150                 }
7151         }
7152     },
7153
7154     /**
7155      * Finds the first child that has the attribute with the specified value.
7156      * @param {String} attribute The attribute name
7157      * @param {Mixed} value The value to search for
7158      * @return {Node} The found child or null if none was found
7159      */
7160     findChild : function(attribute, value){
7161         var cs = this.childNodes;
7162         for(var i = 0, len = cs.length; i < len; i++) {
7163                 if(cs[i].attributes[attribute] == value){
7164                     return cs[i];
7165                 }
7166         }
7167         return null;
7168     },
7169
7170     /**
7171      * Finds the first child by a custom function. The child matches if the function passed
7172      * returns true.
7173      * @param {Function} fn
7174      * @param {Object} scope (optional)
7175      * @return {Node} The found child or null if none was found
7176      */
7177     findChildBy : function(fn, scope){
7178         var cs = this.childNodes;
7179         for(var i = 0, len = cs.length; i < len; i++) {
7180                 if(fn.call(scope||cs[i], cs[i]) === true){
7181                     return cs[i];
7182                 }
7183         }
7184         return null;
7185     },
7186
7187     /**
7188      * Sorts this nodes children using the supplied sort function
7189      * @param {Function} fn
7190      * @param {Object} scope (optional)
7191      */
7192     sort : function(fn, scope){
7193         var cs = this.childNodes;
7194         var len = cs.length;
7195         if(len > 0){
7196             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
7197             cs.sort(sortFn);
7198             for(var i = 0; i < len; i++){
7199                 var n = cs[i];
7200                 n.previousSibling = cs[i-1];
7201                 n.nextSibling = cs[i+1];
7202                 if(i == 0){
7203                     this.setFirstChild(n);
7204                 }
7205                 if(i == len-1){
7206                     this.setLastChild(n);
7207                 }
7208             }
7209         }
7210     },
7211
7212     /**
7213      * Returns true if this node is an ancestor (at any point) of the passed node.
7214      * @param {Node} node
7215      * @return {Boolean}
7216      */
7217     contains : function(node){
7218         return node.isAncestor(this);
7219     },
7220
7221     /**
7222      * Returns true if the passed node is an ancestor (at any point) of this node.
7223      * @param {Node} node
7224      * @return {Boolean}
7225      */
7226     isAncestor : function(node){
7227         var p = this.parentNode;
7228         while(p){
7229             if(p == node){
7230                 return true;
7231             }
7232             p = p.parentNode;
7233         }
7234         return false;
7235     },
7236
7237     toString : function(){
7238         return "[Node"+(this.id?" "+this.id:"")+"]";
7239     }
7240 });/*
7241  * Based on:
7242  * Ext JS Library 1.1.1
7243  * Copyright(c) 2006-2007, Ext JS, LLC.
7244  *
7245  * Originally Released Under LGPL - original licence link has changed is not relivant.
7246  *
7247  * Fork - LGPL
7248  * <script type="text/javascript">
7249  */
7250  
7251
7252 /**
7253  * @class Roo.ComponentMgr
7254  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
7255  * @singleton
7256  */
7257 Roo.ComponentMgr = function(){
7258     var all = new Roo.util.MixedCollection();
7259
7260     return {
7261         /**
7262          * Registers a component.
7263          * @param {Roo.Component} c The component
7264          */
7265         register : function(c){
7266             all.add(c);
7267         },
7268
7269         /**
7270          * Unregisters a component.
7271          * @param {Roo.Component} c The component
7272          */
7273         unregister : function(c){
7274             all.remove(c);
7275         },
7276
7277         /**
7278          * Returns a component by id
7279          * @param {String} id The component id
7280          */
7281         get : function(id){
7282             return all.get(id);
7283         },
7284
7285         /**
7286          * Registers a function that will be called when a specified component is added to ComponentMgr
7287          * @param {String} id The component id
7288          * @param {Funtction} fn The callback function
7289          * @param {Object} scope The scope of the callback
7290          */
7291         onAvailable : function(id, fn, scope){
7292             all.on("add", function(index, o){
7293                 if(o.id == id){
7294                     fn.call(scope || o, o);
7295                     all.un("add", fn, scope);
7296                 }
7297             });
7298         }
7299     };
7300 }();/*
7301  * Based on:
7302  * Ext JS Library 1.1.1
7303  * Copyright(c) 2006-2007, Ext JS, LLC.
7304  *
7305  * Originally Released Under LGPL - original licence link has changed is not relivant.
7306  *
7307  * Fork - LGPL
7308  * <script type="text/javascript">
7309  */
7310  
7311 /**
7312  * @class Roo.Component
7313  * @extends Roo.util.Observable
7314  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
7315  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
7316  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
7317  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
7318  * All visual components (widgets) that require rendering into a layout should subclass Component.
7319  * @constructor
7320  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
7321  * 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
7322  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
7323  */
7324 Roo.Component = function(config){
7325     config = config || {};
7326     if(config.tagName || config.dom || typeof config == "string"){ // element object
7327         config = {el: config, id: config.id || config};
7328     }
7329     this.initialConfig = config;
7330
7331     Roo.apply(this, config);
7332     this.addEvents({
7333         /**
7334          * @event disable
7335          * Fires after the component is disabled.
7336              * @param {Roo.Component} this
7337              */
7338         disable : true,
7339         /**
7340          * @event enable
7341          * Fires after the component is enabled.
7342              * @param {Roo.Component} this
7343              */
7344         enable : true,
7345         /**
7346          * @event beforeshow
7347          * Fires before the component is shown.  Return false to stop the show.
7348              * @param {Roo.Component} this
7349              */
7350         beforeshow : true,
7351         /**
7352          * @event show
7353          * Fires after the component is shown.
7354              * @param {Roo.Component} this
7355              */
7356         show : true,
7357         /**
7358          * @event beforehide
7359          * Fires before the component is hidden. Return false to stop the hide.
7360              * @param {Roo.Component} this
7361              */
7362         beforehide : true,
7363         /**
7364          * @event hide
7365          * Fires after the component is hidden.
7366              * @param {Roo.Component} this
7367              */
7368         hide : true,
7369         /**
7370          * @event beforerender
7371          * Fires before the component is rendered. Return false to stop the render.
7372              * @param {Roo.Component} this
7373              */
7374         beforerender : true,
7375         /**
7376          * @event render
7377          * Fires after the component is rendered.
7378              * @param {Roo.Component} this
7379              */
7380         render : true,
7381         /**
7382          * @event beforedestroy
7383          * Fires before the component is destroyed. Return false to stop the destroy.
7384              * @param {Roo.Component} this
7385              */
7386         beforedestroy : true,
7387         /**
7388          * @event destroy
7389          * Fires after the component is destroyed.
7390              * @param {Roo.Component} this
7391              */
7392         destroy : true
7393     });
7394     if(!this.id){
7395         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
7396     }
7397     Roo.ComponentMgr.register(this);
7398     Roo.Component.superclass.constructor.call(this);
7399     this.initComponent();
7400     if(this.renderTo){ // not supported by all components yet. use at your own risk!
7401         this.render(this.renderTo);
7402         delete this.renderTo;
7403     }
7404 };
7405
7406 /** @private */
7407 Roo.Component.AUTO_ID = 1000;
7408
7409 Roo.extend(Roo.Component, Roo.util.Observable, {
7410     /**
7411      * @scope Roo.Component.prototype
7412      * @type {Boolean}
7413      * true if this component is hidden. Read-only.
7414      */
7415     hidden : false,
7416     /**
7417      * @type {Boolean}
7418      * true if this component is disabled. Read-only.
7419      */
7420     disabled : false,
7421     /**
7422      * @type {Boolean}
7423      * true if this component has been rendered. Read-only.
7424      */
7425     rendered : false,
7426     
7427     /** @cfg {String} disableClass
7428      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
7429      */
7430     disabledClass : "x-item-disabled",
7431         /** @cfg {Boolean} allowDomMove
7432          * Whether the component can move the Dom node when rendering (defaults to true).
7433          */
7434     allowDomMove : true,
7435     /** @cfg {String} hideMode
7436      * How this component should hidden. Supported values are
7437      * "visibility" (css visibility), "offsets" (negative offset position) and
7438      * "display" (css display) - defaults to "display".
7439      */
7440     hideMode: 'display',
7441
7442     /** @private */
7443     ctype : "Roo.Component",
7444
7445     /**
7446      * @cfg {String} actionMode 
7447      * which property holds the element that used for  hide() / show() / disable() / enable()
7448      * default is 'el' 
7449      */
7450     actionMode : "el",
7451
7452     /** @private */
7453     getActionEl : function(){
7454         return this[this.actionMode];
7455     },
7456
7457     initComponent : Roo.emptyFn,
7458     /**
7459      * If this is a lazy rendering component, render it to its container element.
7460      * @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.
7461      */
7462     render : function(container, position){
7463         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
7464             if(!container && this.el){
7465                 this.el = Roo.get(this.el);
7466                 container = this.el.dom.parentNode;
7467                 this.allowDomMove = false;
7468             }
7469             this.container = Roo.get(container);
7470             this.rendered = true;
7471             if(position !== undefined){
7472                 if(typeof position == 'number'){
7473                     position = this.container.dom.childNodes[position];
7474                 }else{
7475                     position = Roo.getDom(position);
7476                 }
7477             }
7478             this.onRender(this.container, position || null);
7479             if(this.cls){
7480                 this.el.addClass(this.cls);
7481                 delete this.cls;
7482             }
7483             if(this.style){
7484                 this.el.applyStyles(this.style);
7485                 delete this.style;
7486             }
7487             this.fireEvent("render", this);
7488             this.afterRender(this.container);
7489             if(this.hidden){
7490                 this.hide();
7491             }
7492             if(this.disabled){
7493                 this.disable();
7494             }
7495         }
7496         return this;
7497     },
7498
7499     /** @private */
7500     // default function is not really useful
7501     onRender : function(ct, position){
7502         if(this.el){
7503             this.el = Roo.get(this.el);
7504             if(this.allowDomMove !== false){
7505                 ct.dom.insertBefore(this.el.dom, position);
7506             }
7507         }
7508     },
7509
7510     /** @private */
7511     getAutoCreate : function(){
7512         var cfg = typeof this.autoCreate == "object" ?
7513                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
7514         if(this.id && !cfg.id){
7515             cfg.id = this.id;
7516         }
7517         return cfg;
7518     },
7519
7520     /** @private */
7521     afterRender : Roo.emptyFn,
7522
7523     /**
7524      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
7525      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
7526      */
7527     destroy : function(){
7528         if(this.fireEvent("beforedestroy", this) !== false){
7529             this.purgeListeners();
7530             this.beforeDestroy();
7531             if(this.rendered){
7532                 this.el.removeAllListeners();
7533                 this.el.remove();
7534                 if(this.actionMode == "container"){
7535                     this.container.remove();
7536                 }
7537             }
7538             this.onDestroy();
7539             Roo.ComponentMgr.unregister(this);
7540             this.fireEvent("destroy", this);
7541         }
7542     },
7543
7544         /** @private */
7545     beforeDestroy : function(){
7546
7547     },
7548
7549         /** @private */
7550         onDestroy : function(){
7551
7552     },
7553
7554     /**
7555      * Returns the underlying {@link Roo.Element}.
7556      * @return {Roo.Element} The element
7557      */
7558     getEl : function(){
7559         return this.el;
7560     },
7561
7562     /**
7563      * Returns the id of this component.
7564      * @return {String}
7565      */
7566     getId : function(){
7567         return this.id;
7568     },
7569
7570     /**
7571      * Try to focus this component.
7572      * @param {Boolean} selectText True to also select the text in this component (if applicable)
7573      * @return {Roo.Component} this
7574      */
7575     focus : function(selectText){
7576         if(this.rendered){
7577             this.el.focus();
7578             if(selectText === true){
7579                 this.el.dom.select();
7580             }
7581         }
7582         return this;
7583     },
7584
7585     /** @private */
7586     blur : function(){
7587         if(this.rendered){
7588             this.el.blur();
7589         }
7590         return this;
7591     },
7592
7593     /**
7594      * Disable this component.
7595      * @return {Roo.Component} this
7596      */
7597     disable : function(){
7598         if(this.rendered){
7599             this.onDisable();
7600         }
7601         this.disabled = true;
7602         this.fireEvent("disable", this);
7603         return this;
7604     },
7605
7606         // private
7607     onDisable : function(){
7608         this.getActionEl().addClass(this.disabledClass);
7609         this.el.dom.disabled = true;
7610     },
7611
7612     /**
7613      * Enable this component.
7614      * @return {Roo.Component} this
7615      */
7616     enable : function(){
7617         if(this.rendered){
7618             this.onEnable();
7619         }
7620         this.disabled = false;
7621         this.fireEvent("enable", this);
7622         return this;
7623     },
7624
7625         // private
7626     onEnable : function(){
7627         this.getActionEl().removeClass(this.disabledClass);
7628         this.el.dom.disabled = false;
7629     },
7630
7631     /**
7632      * Convenience function for setting disabled/enabled by boolean.
7633      * @param {Boolean} disabled
7634      */
7635     setDisabled : function(disabled){
7636         this[disabled ? "disable" : "enable"]();
7637     },
7638
7639     /**
7640      * Show this component.
7641      * @return {Roo.Component} this
7642      */
7643     show: function(){
7644         if(this.fireEvent("beforeshow", this) !== false){
7645             this.hidden = false;
7646             if(this.rendered){
7647                 this.onShow();
7648             }
7649             this.fireEvent("show", this);
7650         }
7651         return this;
7652     },
7653
7654     // private
7655     onShow : function(){
7656         var ae = this.getActionEl();
7657         if(this.hideMode == 'visibility'){
7658             ae.dom.style.visibility = "visible";
7659         }else if(this.hideMode == 'offsets'){
7660             ae.removeClass('x-hidden');
7661         }else{
7662             ae.dom.style.display = "";
7663         }
7664     },
7665
7666     /**
7667      * Hide this component.
7668      * @return {Roo.Component} this
7669      */
7670     hide: function(){
7671         if(this.fireEvent("beforehide", this) !== false){
7672             this.hidden = true;
7673             if(this.rendered){
7674                 this.onHide();
7675             }
7676             this.fireEvent("hide", this);
7677         }
7678         return this;
7679     },
7680
7681     // private
7682     onHide : function(){
7683         var ae = this.getActionEl();
7684         if(this.hideMode == 'visibility'){
7685             ae.dom.style.visibility = "hidden";
7686         }else if(this.hideMode == 'offsets'){
7687             ae.addClass('x-hidden');
7688         }else{
7689             ae.dom.style.display = "none";
7690         }
7691     },
7692
7693     /**
7694      * Convenience function to hide or show this component by boolean.
7695      * @param {Boolean} visible True to show, false to hide
7696      * @return {Roo.Component} this
7697      */
7698     setVisible: function(visible){
7699         if(visible) {
7700             this.show();
7701         }else{
7702             this.hide();
7703         }
7704         return this;
7705     },
7706
7707     /**
7708      * Returns true if this component is visible.
7709      */
7710     isVisible : function(){
7711         return this.getActionEl().isVisible();
7712     },
7713
7714     cloneConfig : function(overrides){
7715         overrides = overrides || {};
7716         var id = overrides.id || Roo.id();
7717         var cfg = Roo.applyIf(overrides, this.initialConfig);
7718         cfg.id = id; // prevent dup id
7719         return new this.constructor(cfg);
7720     }
7721 });/*
7722  * Based on:
7723  * Ext JS Library 1.1.1
7724  * Copyright(c) 2006-2007, Ext JS, LLC.
7725  *
7726  * Originally Released Under LGPL - original licence link has changed is not relivant.
7727  *
7728  * Fork - LGPL
7729  * <script type="text/javascript">
7730  */
7731  (function(){ 
7732 /**
7733  * @class Roo.Layer
7734  * @extends Roo.Element
7735  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
7736  * automatic maintaining of shadow/shim positions.
7737  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
7738  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
7739  * you can pass a string with a CSS class name. False turns off the shadow.
7740  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
7741  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
7742  * @cfg {String} cls CSS class to add to the element
7743  * @cfg {Number} zindex Starting z-index (defaults to 11000)
7744  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
7745  * @constructor
7746  * @param {Object} config An object with config options.
7747  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
7748  */
7749
7750 Roo.Layer = function(config, existingEl){
7751     config = config || {};
7752     var dh = Roo.DomHelper;
7753     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
7754     if(existingEl){
7755         this.dom = Roo.getDom(existingEl);
7756     }
7757     if(!this.dom){
7758         var o = config.dh || {tag: "div", cls: "x-layer"};
7759         this.dom = dh.append(pel, o);
7760     }
7761     if(config.cls){
7762         this.addClass(config.cls);
7763     }
7764     this.constrain = config.constrain !== false;
7765     this.visibilityMode = Roo.Element.VISIBILITY;
7766     if(config.id){
7767         this.id = this.dom.id = config.id;
7768     }else{
7769         this.id = Roo.id(this.dom);
7770     }
7771     this.zindex = config.zindex || this.getZIndex();
7772     this.position("absolute", this.zindex);
7773     if(config.shadow){
7774         this.shadowOffset = config.shadowOffset || 4;
7775         this.shadow = new Roo.Shadow({
7776             offset : this.shadowOffset,
7777             mode : config.shadow
7778         });
7779     }else{
7780         this.shadowOffset = 0;
7781     }
7782     this.useShim = config.shim !== false && Roo.useShims;
7783     this.useDisplay = config.useDisplay;
7784     this.hide();
7785 };
7786
7787 var supr = Roo.Element.prototype;
7788
7789 // shims are shared among layer to keep from having 100 iframes
7790 var shims = [];
7791
7792 Roo.extend(Roo.Layer, Roo.Element, {
7793
7794     getZIndex : function(){
7795         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
7796     },
7797
7798     getShim : function(){
7799         if(!this.useShim){
7800             return null;
7801         }
7802         if(this.shim){
7803             return this.shim;
7804         }
7805         var shim = shims.shift();
7806         if(!shim){
7807             shim = this.createShim();
7808             shim.enableDisplayMode('block');
7809             shim.dom.style.display = 'none';
7810             shim.dom.style.visibility = 'visible';
7811         }
7812         var pn = this.dom.parentNode;
7813         if(shim.dom.parentNode != pn){
7814             pn.insertBefore(shim.dom, this.dom);
7815         }
7816         shim.setStyle('z-index', this.getZIndex()-2);
7817         this.shim = shim;
7818         return shim;
7819     },
7820
7821     hideShim : function(){
7822         if(this.shim){
7823             this.shim.setDisplayed(false);
7824             shims.push(this.shim);
7825             delete this.shim;
7826         }
7827     },
7828
7829     disableShadow : function(){
7830         if(this.shadow){
7831             this.shadowDisabled = true;
7832             this.shadow.hide();
7833             this.lastShadowOffset = this.shadowOffset;
7834             this.shadowOffset = 0;
7835         }
7836     },
7837
7838     enableShadow : function(show){
7839         if(this.shadow){
7840             this.shadowDisabled = false;
7841             this.shadowOffset = this.lastShadowOffset;
7842             delete this.lastShadowOffset;
7843             if(show){
7844                 this.sync(true);
7845             }
7846         }
7847     },
7848
7849     // private
7850     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
7851     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
7852     sync : function(doShow){
7853         var sw = this.shadow;
7854         if(!this.updating && this.isVisible() && (sw || this.useShim)){
7855             var sh = this.getShim();
7856
7857             var w = this.getWidth(),
7858                 h = this.getHeight();
7859
7860             var l = this.getLeft(true),
7861                 t = this.getTop(true);
7862
7863             if(sw && !this.shadowDisabled){
7864                 if(doShow && !sw.isVisible()){
7865                     sw.show(this);
7866                 }else{
7867                     sw.realign(l, t, w, h);
7868                 }
7869                 if(sh){
7870                     if(doShow){
7871                        sh.show();
7872                     }
7873                     // fit the shim behind the shadow, so it is shimmed too
7874                     var a = sw.adjusts, s = sh.dom.style;
7875                     s.left = (Math.min(l, l+a.l))+"px";
7876                     s.top = (Math.min(t, t+a.t))+"px";
7877                     s.width = (w+a.w)+"px";
7878                     s.height = (h+a.h)+"px";
7879                 }
7880             }else if(sh){
7881                 if(doShow){
7882                    sh.show();
7883                 }
7884                 sh.setSize(w, h);
7885                 sh.setLeftTop(l, t);
7886             }
7887             
7888         }
7889     },
7890
7891     // private
7892     destroy : function(){
7893         this.hideShim();
7894         if(this.shadow){
7895             this.shadow.hide();
7896         }
7897         this.removeAllListeners();
7898         var pn = this.dom.parentNode;
7899         if(pn){
7900             pn.removeChild(this.dom);
7901         }
7902         Roo.Element.uncache(this.id);
7903     },
7904
7905     remove : function(){
7906         this.destroy();
7907     },
7908
7909     // private
7910     beginUpdate : function(){
7911         this.updating = true;
7912     },
7913
7914     // private
7915     endUpdate : function(){
7916         this.updating = false;
7917         this.sync(true);
7918     },
7919
7920     // private
7921     hideUnders : function(negOffset){
7922         if(this.shadow){
7923             this.shadow.hide();
7924         }
7925         this.hideShim();
7926     },
7927
7928     // private
7929     constrainXY : function(){
7930         if(this.constrain){
7931             var vw = Roo.lib.Dom.getViewWidth(),
7932                 vh = Roo.lib.Dom.getViewHeight();
7933             var s = Roo.get(document).getScroll();
7934
7935             var xy = this.getXY();
7936             var x = xy[0], y = xy[1];   
7937             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
7938             // only move it if it needs it
7939             var moved = false;
7940             // first validate right/bottom
7941             if((x + w) > vw+s.left){
7942                 x = vw - w - this.shadowOffset;
7943                 moved = true;
7944             }
7945             if((y + h) > vh+s.top){
7946                 y = vh - h - this.shadowOffset;
7947                 moved = true;
7948             }
7949             // then make sure top/left isn't negative
7950             if(x < s.left){
7951                 x = s.left;
7952                 moved = true;
7953             }
7954             if(y < s.top){
7955                 y = s.top;
7956                 moved = true;
7957             }
7958             if(moved){
7959                 if(this.avoidY){
7960                     var ay = this.avoidY;
7961                     if(y <= ay && (y+h) >= ay){
7962                         y = ay-h-5;   
7963                     }
7964                 }
7965                 xy = [x, y];
7966                 this.storeXY(xy);
7967                 supr.setXY.call(this, xy);
7968                 this.sync();
7969             }
7970         }
7971     },
7972
7973     isVisible : function(){
7974         return this.visible;    
7975     },
7976
7977     // private
7978     showAction : function(){
7979         this.visible = true; // track visibility to prevent getStyle calls
7980         if(this.useDisplay === true){
7981             this.setDisplayed("");
7982         }else if(this.lastXY){
7983             supr.setXY.call(this, this.lastXY);
7984         }else if(this.lastLT){
7985             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
7986         }
7987     },
7988
7989     // private
7990     hideAction : function(){
7991         this.visible = false;
7992         if(this.useDisplay === true){
7993             this.setDisplayed(false);
7994         }else{
7995             this.setLeftTop(-10000,-10000);
7996         }
7997     },
7998
7999     // overridden Element method
8000     setVisible : function(v, a, d, c, e){
8001         if(v){
8002             this.showAction();
8003         }
8004         if(a && v){
8005             var cb = function(){
8006                 this.sync(true);
8007                 if(c){
8008                     c();
8009                 }
8010             }.createDelegate(this);
8011             supr.setVisible.call(this, true, true, d, cb, e);
8012         }else{
8013             if(!v){
8014                 this.hideUnders(true);
8015             }
8016             var cb = c;
8017             if(a){
8018                 cb = function(){
8019                     this.hideAction();
8020                     if(c){
8021                         c();
8022                     }
8023                 }.createDelegate(this);
8024             }
8025             supr.setVisible.call(this, v, a, d, cb, e);
8026             if(v){
8027                 this.sync(true);
8028             }else if(!a){
8029                 this.hideAction();
8030             }
8031         }
8032     },
8033
8034     storeXY : function(xy){
8035         delete this.lastLT;
8036         this.lastXY = xy;
8037     },
8038
8039     storeLeftTop : function(left, top){
8040         delete this.lastXY;
8041         this.lastLT = [left, top];
8042     },
8043
8044     // private
8045     beforeFx : function(){
8046         this.beforeAction();
8047         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
8048     },
8049
8050     // private
8051     afterFx : function(){
8052         Roo.Layer.superclass.afterFx.apply(this, arguments);
8053         this.sync(this.isVisible());
8054     },
8055
8056     // private
8057     beforeAction : function(){
8058         if(!this.updating && this.shadow){
8059             this.shadow.hide();
8060         }
8061     },
8062
8063     // overridden Element method
8064     setLeft : function(left){
8065         this.storeLeftTop(left, this.getTop(true));
8066         supr.setLeft.apply(this, arguments);
8067         this.sync();
8068     },
8069
8070     setTop : function(top){
8071         this.storeLeftTop(this.getLeft(true), top);
8072         supr.setTop.apply(this, arguments);
8073         this.sync();
8074     },
8075
8076     setLeftTop : function(left, top){
8077         this.storeLeftTop(left, top);
8078         supr.setLeftTop.apply(this, arguments);
8079         this.sync();
8080     },
8081
8082     setXY : function(xy, a, d, c, e){
8083         this.fixDisplay();
8084         this.beforeAction();
8085         this.storeXY(xy);
8086         var cb = this.createCB(c);
8087         supr.setXY.call(this, xy, a, d, cb, e);
8088         if(!a){
8089             cb();
8090         }
8091     },
8092
8093     // private
8094     createCB : function(c){
8095         var el = this;
8096         return function(){
8097             el.constrainXY();
8098             el.sync(true);
8099             if(c){
8100                 c();
8101             }
8102         };
8103     },
8104
8105     // overridden Element method
8106     setX : function(x, a, d, c, e){
8107         this.setXY([x, this.getY()], a, d, c, e);
8108     },
8109
8110     // overridden Element method
8111     setY : function(y, a, d, c, e){
8112         this.setXY([this.getX(), y], a, d, c, e);
8113     },
8114
8115     // overridden Element method
8116     setSize : function(w, h, a, d, c, e){
8117         this.beforeAction();
8118         var cb = this.createCB(c);
8119         supr.setSize.call(this, w, h, a, d, cb, e);
8120         if(!a){
8121             cb();
8122         }
8123     },
8124
8125     // overridden Element method
8126     setWidth : function(w, a, d, c, e){
8127         this.beforeAction();
8128         var cb = this.createCB(c);
8129         supr.setWidth.call(this, w, a, d, cb, e);
8130         if(!a){
8131             cb();
8132         }
8133     },
8134
8135     // overridden Element method
8136     setHeight : function(h, a, d, c, e){
8137         this.beforeAction();
8138         var cb = this.createCB(c);
8139         supr.setHeight.call(this, h, a, d, cb, e);
8140         if(!a){
8141             cb();
8142         }
8143     },
8144
8145     // overridden Element method
8146     setBounds : function(x, y, w, h, a, d, c, e){
8147         this.beforeAction();
8148         var cb = this.createCB(c);
8149         if(!a){
8150             this.storeXY([x, y]);
8151             supr.setXY.call(this, [x, y]);
8152             supr.setSize.call(this, w, h, a, d, cb, e);
8153             cb();
8154         }else{
8155             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
8156         }
8157         return this;
8158     },
8159     
8160     /**
8161      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
8162      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
8163      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
8164      * @param {Number} zindex The new z-index to set
8165      * @return {this} The Layer
8166      */
8167     setZIndex : function(zindex){
8168         this.zindex = zindex;
8169         this.setStyle("z-index", zindex + 2);
8170         if(this.shadow){
8171             this.shadow.setZIndex(zindex + 1);
8172         }
8173         if(this.shim){
8174             this.shim.setStyle("z-index", zindex);
8175         }
8176     }
8177 });
8178 })();/*
8179  * Based on:
8180  * Ext JS Library 1.1.1
8181  * Copyright(c) 2006-2007, Ext JS, LLC.
8182  *
8183  * Originally Released Under LGPL - original licence link has changed is not relivant.
8184  *
8185  * Fork - LGPL
8186  * <script type="text/javascript">
8187  */
8188
8189
8190 /**
8191  * @class Roo.Shadow
8192  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
8193  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
8194  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
8195  * @constructor
8196  * Create a new Shadow
8197  * @param {Object} config The config object
8198  */
8199 Roo.Shadow = function(config){
8200     Roo.apply(this, config);
8201     if(typeof this.mode != "string"){
8202         this.mode = this.defaultMode;
8203     }
8204     var o = this.offset, a = {h: 0};
8205     var rad = Math.floor(this.offset/2);
8206     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
8207         case "drop":
8208             a.w = 0;
8209             a.l = a.t = o;
8210             a.t -= 1;
8211             if(Roo.isIE){
8212                 a.l -= this.offset + rad;
8213                 a.t -= this.offset + rad;
8214                 a.w -= rad;
8215                 a.h -= rad;
8216                 a.t += 1;
8217             }
8218         break;
8219         case "sides":
8220             a.w = (o*2);
8221             a.l = -o;
8222             a.t = o-1;
8223             if(Roo.isIE){
8224                 a.l -= (this.offset - rad);
8225                 a.t -= this.offset + rad;
8226                 a.l += 1;
8227                 a.w -= (this.offset - rad)*2;
8228                 a.w -= rad + 1;
8229                 a.h -= 1;
8230             }
8231         break;
8232         case "frame":
8233             a.w = a.h = (o*2);
8234             a.l = a.t = -o;
8235             a.t += 1;
8236             a.h -= 2;
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 + 1);
8242                 a.h -= (this.offset + rad);
8243                 a.h += 1;
8244             }
8245         break;
8246     };
8247
8248     this.adjusts = a;
8249 };
8250
8251 Roo.Shadow.prototype = {
8252     /**
8253      * @cfg {String} mode
8254      * The shadow display mode.  Supports the following options:<br />
8255      * sides: Shadow displays on both sides and bottom only<br />
8256      * frame: Shadow displays equally on all four sides<br />
8257      * drop: Traditional bottom-right drop shadow (default)
8258      */
8259     /**
8260      * @cfg {String} offset
8261      * The number of pixels to offset the shadow from the element (defaults to 4)
8262      */
8263     offset: 4,
8264
8265     // private
8266     defaultMode: "drop",
8267
8268     /**
8269      * Displays the shadow under the target element
8270      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
8271      */
8272     show : function(target){
8273         target = Roo.get(target);
8274         if(!this.el){
8275             this.el = Roo.Shadow.Pool.pull();
8276             if(this.el.dom.nextSibling != target.dom){
8277                 this.el.insertBefore(target);
8278             }
8279         }
8280         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
8281         if(Roo.isIE){
8282             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
8283         }
8284         this.realign(
8285             target.getLeft(true),
8286             target.getTop(true),
8287             target.getWidth(),
8288             target.getHeight()
8289         );
8290         this.el.dom.style.display = "block";
8291     },
8292
8293     /**
8294      * Returns true if the shadow is visible, else false
8295      */
8296     isVisible : function(){
8297         return this.el ? true : false;  
8298     },
8299
8300     /**
8301      * Direct alignment when values are already available. Show must be called at least once before
8302      * calling this method to ensure it is initialized.
8303      * @param {Number} left The target element left position
8304      * @param {Number} top The target element top position
8305      * @param {Number} width The target element width
8306      * @param {Number} height The target element height
8307      */
8308     realign : function(l, t, w, h){
8309         if(!this.el){
8310             return;
8311         }
8312         var a = this.adjusts, d = this.el.dom, s = d.style;
8313         var iea = 0;
8314         s.left = (l+a.l)+"px";
8315         s.top = (t+a.t)+"px";
8316         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
8317  
8318         if(s.width != sws || s.height != shs){
8319             s.width = sws;
8320             s.height = shs;
8321             if(!Roo.isIE){
8322                 var cn = d.childNodes;
8323                 var sww = Math.max(0, (sw-12))+"px";
8324                 cn[0].childNodes[1].style.width = sww;
8325                 cn[1].childNodes[1].style.width = sww;
8326                 cn[2].childNodes[1].style.width = sww;
8327                 cn[1].style.height = Math.max(0, (sh-12))+"px";
8328             }
8329         }
8330     },
8331
8332     /**
8333      * Hides this shadow
8334      */
8335     hide : function(){
8336         if(this.el){
8337             this.el.dom.style.display = "none";
8338             Roo.Shadow.Pool.push(this.el);
8339             delete this.el;
8340         }
8341     },
8342
8343     /**
8344      * Adjust the z-index of this shadow
8345      * @param {Number} zindex The new z-index
8346      */
8347     setZIndex : function(z){
8348         this.zIndex = z;
8349         if(this.el){
8350             this.el.setStyle("z-index", z);
8351         }
8352     }
8353 };
8354
8355 // Private utility class that manages the internal Shadow cache
8356 Roo.Shadow.Pool = function(){
8357     var p = [];
8358     var markup = Roo.isIE ?
8359                  '<div class="x-ie-shadow"></div>' :
8360                  '<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>';
8361     return {
8362         pull : function(){
8363             var sh = p.shift();
8364             if(!sh){
8365                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
8366                 sh.autoBoxAdjust = false;
8367             }
8368             return sh;
8369         },
8370
8371         push : function(sh){
8372             p.push(sh);
8373         }
8374     };
8375 }();/*
8376  * Based on:
8377  * Ext JS Library 1.1.1
8378  * Copyright(c) 2006-2007, Ext JS, LLC.
8379  *
8380  * Originally Released Under LGPL - original licence link has changed is not relivant.
8381  *
8382  * Fork - LGPL
8383  * <script type="text/javascript">
8384  */
8385
8386 /**
8387  * @class Roo.BoxComponent
8388  * @extends Roo.Component
8389  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
8390  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
8391  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
8392  * layout containers.
8393  * @constructor
8394  * @param {Roo.Element/String/Object} config The configuration options.
8395  */
8396 Roo.BoxComponent = function(config){
8397     Roo.Component.call(this, config);
8398     this.addEvents({
8399         /**
8400          * @event resize
8401          * Fires after the component is resized.
8402              * @param {Roo.Component} this
8403              * @param {Number} adjWidth The box-adjusted width that was set
8404              * @param {Number} adjHeight The box-adjusted height that was set
8405              * @param {Number} rawWidth The width that was originally specified
8406              * @param {Number} rawHeight The height that was originally specified
8407              */
8408         resize : true,
8409         /**
8410          * @event move
8411          * Fires after the component is moved.
8412              * @param {Roo.Component} this
8413              * @param {Number} x The new x position
8414              * @param {Number} y The new y position
8415              */
8416         move : true
8417     });
8418 };
8419
8420 Roo.extend(Roo.BoxComponent, Roo.Component, {
8421     // private, set in afterRender to signify that the component has been rendered
8422     boxReady : false,
8423     // private, used to defer height settings to subclasses
8424     deferHeight: false,
8425     /** @cfg {Number} width
8426      * width (optional) size of component
8427      */
8428      /** @cfg {Number} height
8429      * height (optional) size of component
8430      */
8431      
8432     /**
8433      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
8434      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
8435      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
8436      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
8437      * @return {Roo.BoxComponent} this
8438      */
8439     setSize : function(w, h){
8440         // support for standard size objects
8441         if(typeof w == 'object'){
8442             h = w.height;
8443             w = w.width;
8444         }
8445         // not rendered
8446         if(!this.boxReady){
8447             this.width = w;
8448             this.height = h;
8449             return this;
8450         }
8451
8452         // prevent recalcs when not needed
8453         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
8454             return this;
8455         }
8456         this.lastSize = {width: w, height: h};
8457
8458         var adj = this.adjustSize(w, h);
8459         var aw = adj.width, ah = adj.height;
8460         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
8461             var rz = this.getResizeEl();
8462             if(!this.deferHeight && aw !== undefined && ah !== undefined){
8463                 rz.setSize(aw, ah);
8464             }else if(!this.deferHeight && ah !== undefined){
8465                 rz.setHeight(ah);
8466             }else if(aw !== undefined){
8467                 rz.setWidth(aw);
8468             }
8469             this.onResize(aw, ah, w, h);
8470             this.fireEvent('resize', this, aw, ah, w, h);
8471         }
8472         return this;
8473     },
8474
8475     /**
8476      * Gets the current size of the component's underlying element.
8477      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8478      */
8479     getSize : function(){
8480         return this.el.getSize();
8481     },
8482
8483     /**
8484      * Gets the current XY position of the component's underlying element.
8485      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8486      * @return {Array} The XY position of the element (e.g., [100, 200])
8487      */
8488     getPosition : function(local){
8489         if(local === true){
8490             return [this.el.getLeft(true), this.el.getTop(true)];
8491         }
8492         return this.xy || this.el.getXY();
8493     },
8494
8495     /**
8496      * Gets the current box measurements of the component's underlying element.
8497      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8498      * @returns {Object} box An object in the format {x, y, width, height}
8499      */
8500     getBox : function(local){
8501         var s = this.el.getSize();
8502         if(local){
8503             s.x = this.el.getLeft(true);
8504             s.y = this.el.getTop(true);
8505         }else{
8506             var xy = this.xy || this.el.getXY();
8507             s.x = xy[0];
8508             s.y = xy[1];
8509         }
8510         return s;
8511     },
8512
8513     /**
8514      * Sets the current box measurements of the component's underlying element.
8515      * @param {Object} box An object in the format {x, y, width, height}
8516      * @returns {Roo.BoxComponent} this
8517      */
8518     updateBox : function(box){
8519         this.setSize(box.width, box.height);
8520         this.setPagePosition(box.x, box.y);
8521         return this;
8522     },
8523
8524     // protected
8525     getResizeEl : function(){
8526         return this.resizeEl || this.el;
8527     },
8528
8529     // protected
8530     getPositionEl : function(){
8531         return this.positionEl || this.el;
8532     },
8533
8534     /**
8535      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
8536      * This method fires the move event.
8537      * @param {Number} left The new left
8538      * @param {Number} top The new top
8539      * @returns {Roo.BoxComponent} this
8540      */
8541     setPosition : function(x, y){
8542         this.x = x;
8543         this.y = y;
8544         if(!this.boxReady){
8545             return this;
8546         }
8547         var adj = this.adjustPosition(x, y);
8548         var ax = adj.x, ay = adj.y;
8549
8550         var el = this.getPositionEl();
8551         if(ax !== undefined || ay !== undefined){
8552             if(ax !== undefined && ay !== undefined){
8553                 el.setLeftTop(ax, ay);
8554             }else if(ax !== undefined){
8555                 el.setLeft(ax);
8556             }else if(ay !== undefined){
8557                 el.setTop(ay);
8558             }
8559             this.onPosition(ax, ay);
8560             this.fireEvent('move', this, ax, ay);
8561         }
8562         return this;
8563     },
8564
8565     /**
8566      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
8567      * This method fires the move event.
8568      * @param {Number} x The new x position
8569      * @param {Number} y The new y position
8570      * @returns {Roo.BoxComponent} this
8571      */
8572     setPagePosition : function(x, y){
8573         this.pageX = x;
8574         this.pageY = y;
8575         if(!this.boxReady){
8576             return;
8577         }
8578         if(x === undefined || y === undefined){ // cannot translate undefined points
8579             return;
8580         }
8581         var p = this.el.translatePoints(x, y);
8582         this.setPosition(p.left, p.top);
8583         return this;
8584     },
8585
8586     // private
8587     onRender : function(ct, position){
8588         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
8589         if(this.resizeEl){
8590             this.resizeEl = Roo.get(this.resizeEl);
8591         }
8592         if(this.positionEl){
8593             this.positionEl = Roo.get(this.positionEl);
8594         }
8595     },
8596
8597     // private
8598     afterRender : function(){
8599         Roo.BoxComponent.superclass.afterRender.call(this);
8600         this.boxReady = true;
8601         this.setSize(this.width, this.height);
8602         if(this.x || this.y){
8603             this.setPosition(this.x, this.y);
8604         }
8605         if(this.pageX || this.pageY){
8606             this.setPagePosition(this.pageX, this.pageY);
8607         }
8608     },
8609
8610     /**
8611      * Force the component's size to recalculate based on the underlying element's current height and width.
8612      * @returns {Roo.BoxComponent} this
8613      */
8614     syncSize : function(){
8615         delete this.lastSize;
8616         this.setSize(this.el.getWidth(), this.el.getHeight());
8617         return this;
8618     },
8619
8620     /**
8621      * Called after the component is resized, this method is empty by default but can be implemented by any
8622      * subclass that needs to perform custom logic after a resize occurs.
8623      * @param {Number} adjWidth The box-adjusted width that was set
8624      * @param {Number} adjHeight The box-adjusted height that was set
8625      * @param {Number} rawWidth The width that was originally specified
8626      * @param {Number} rawHeight The height that was originally specified
8627      */
8628     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
8629
8630     },
8631
8632     /**
8633      * Called after the component is moved, this method is empty by default but can be implemented by any
8634      * subclass that needs to perform custom logic after a move occurs.
8635      * @param {Number} x The new x position
8636      * @param {Number} y The new y position
8637      */
8638     onPosition : function(x, y){
8639
8640     },
8641
8642     // private
8643     adjustSize : function(w, h){
8644         if(this.autoWidth){
8645             w = 'auto';
8646         }
8647         if(this.autoHeight){
8648             h = 'auto';
8649         }
8650         return {width : w, height: h};
8651     },
8652
8653     // private
8654     adjustPosition : function(x, y){
8655         return {x : x, y: y};
8656     }
8657 });/*
8658  * Based on:
8659  * Ext JS Library 1.1.1
8660  * Copyright(c) 2006-2007, Ext JS, LLC.
8661  *
8662  * Originally Released Under LGPL - original licence link has changed is not relivant.
8663  *
8664  * Fork - LGPL
8665  * <script type="text/javascript">
8666  */
8667
8668
8669 /**
8670  * @class Roo.SplitBar
8671  * @extends Roo.util.Observable
8672  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
8673  * <br><br>
8674  * Usage:
8675  * <pre><code>
8676 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
8677                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
8678 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
8679 split.minSize = 100;
8680 split.maxSize = 600;
8681 split.animate = true;
8682 split.on('moved', splitterMoved);
8683 </code></pre>
8684  * @constructor
8685  * Create a new SplitBar
8686  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
8687  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
8688  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8689  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
8690                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
8691                         position of the SplitBar).
8692  */
8693 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
8694     
8695     /** @private */
8696     this.el = Roo.get(dragElement, true);
8697     this.el.dom.unselectable = "on";
8698     /** @private */
8699     this.resizingEl = Roo.get(resizingElement, true);
8700
8701     /**
8702      * @private
8703      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8704      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
8705      * @type Number
8706      */
8707     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
8708     
8709     /**
8710      * The minimum size of the resizing element. (Defaults to 0)
8711      * @type Number
8712      */
8713     this.minSize = 0;
8714     
8715     /**
8716      * The maximum size of the resizing element. (Defaults to 2000)
8717      * @type Number
8718      */
8719     this.maxSize = 2000;
8720     
8721     /**
8722      * Whether to animate the transition to the new size
8723      * @type Boolean
8724      */
8725     this.animate = false;
8726     
8727     /**
8728      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
8729      * @type Boolean
8730      */
8731     this.useShim = false;
8732     
8733     /** @private */
8734     this.shim = null;
8735     
8736     if(!existingProxy){
8737         /** @private */
8738         this.proxy = Roo.SplitBar.createProxy(this.orientation);
8739     }else{
8740         this.proxy = Roo.get(existingProxy).dom;
8741     }
8742     /** @private */
8743     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
8744     
8745     /** @private */
8746     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
8747     
8748     /** @private */
8749     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
8750     
8751     /** @private */
8752     this.dragSpecs = {};
8753     
8754     /**
8755      * @private The adapter to use to positon and resize elements
8756      */
8757     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
8758     this.adapter.init(this);
8759     
8760     if(this.orientation == Roo.SplitBar.HORIZONTAL){
8761         /** @private */
8762         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
8763         this.el.addClass("x-splitbar-h");
8764     }else{
8765         /** @private */
8766         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
8767         this.el.addClass("x-splitbar-v");
8768     }
8769     
8770     this.addEvents({
8771         /**
8772          * @event resize
8773          * Fires when the splitter is moved (alias for {@link #event-moved})
8774          * @param {Roo.SplitBar} this
8775          * @param {Number} newSize the new width or height
8776          */
8777         "resize" : true,
8778         /**
8779          * @event moved
8780          * Fires when the splitter is moved
8781          * @param {Roo.SplitBar} this
8782          * @param {Number} newSize the new width or height
8783          */
8784         "moved" : true,
8785         /**
8786          * @event beforeresize
8787          * Fires before the splitter is dragged
8788          * @param {Roo.SplitBar} this
8789          */
8790         "beforeresize" : true,
8791
8792         "beforeapply" : true
8793     });
8794
8795     Roo.util.Observable.call(this);
8796 };
8797
8798 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
8799     onStartProxyDrag : function(x, y){
8800         this.fireEvent("beforeresize", this);
8801         if(!this.overlay){
8802             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
8803             o.unselectable();
8804             o.enableDisplayMode("block");
8805             // all splitbars share the same overlay
8806             Roo.SplitBar.prototype.overlay = o;
8807         }
8808         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8809         this.overlay.show();
8810         Roo.get(this.proxy).setDisplayed("block");
8811         var size = this.adapter.getElementSize(this);
8812         this.activeMinSize = this.getMinimumSize();;
8813         this.activeMaxSize = this.getMaximumSize();;
8814         var c1 = size - this.activeMinSize;
8815         var c2 = Math.max(this.activeMaxSize - size, 0);
8816         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8817             this.dd.resetConstraints();
8818             this.dd.setXConstraint(
8819                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
8820                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
8821             );
8822             this.dd.setYConstraint(0, 0);
8823         }else{
8824             this.dd.resetConstraints();
8825             this.dd.setXConstraint(0, 0);
8826             this.dd.setYConstraint(
8827                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
8828                 this.placement == Roo.SplitBar.TOP ? c2 : c1
8829             );
8830          }
8831         this.dragSpecs.startSize = size;
8832         this.dragSpecs.startPoint = [x, y];
8833         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
8834     },
8835     
8836     /** 
8837      * @private Called after the drag operation by the DDProxy
8838      */
8839     onEndProxyDrag : function(e){
8840         Roo.get(this.proxy).setDisplayed(false);
8841         var endPoint = Roo.lib.Event.getXY(e);
8842         if(this.overlay){
8843             this.overlay.hide();
8844         }
8845         var newSize;
8846         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8847             newSize = this.dragSpecs.startSize + 
8848                 (this.placement == Roo.SplitBar.LEFT ?
8849                     endPoint[0] - this.dragSpecs.startPoint[0] :
8850                     this.dragSpecs.startPoint[0] - endPoint[0]
8851                 );
8852         }else{
8853             newSize = this.dragSpecs.startSize + 
8854                 (this.placement == Roo.SplitBar.TOP ?
8855                     endPoint[1] - this.dragSpecs.startPoint[1] :
8856                     this.dragSpecs.startPoint[1] - endPoint[1]
8857                 );
8858         }
8859         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
8860         if(newSize != this.dragSpecs.startSize){
8861             if(this.fireEvent('beforeapply', this, newSize) !== false){
8862                 this.adapter.setElementSize(this, newSize);
8863                 this.fireEvent("moved", this, newSize);
8864                 this.fireEvent("resize", this, newSize);
8865             }
8866         }
8867     },
8868     
8869     /**
8870      * Get the adapter this SplitBar uses
8871      * @return The adapter object
8872      */
8873     getAdapter : function(){
8874         return this.adapter;
8875     },
8876     
8877     /**
8878      * Set the adapter this SplitBar uses
8879      * @param {Object} adapter A SplitBar adapter object
8880      */
8881     setAdapter : function(adapter){
8882         this.adapter = adapter;
8883         this.adapter.init(this);
8884     },
8885     
8886     /**
8887      * Gets the minimum size for the resizing element
8888      * @return {Number} The minimum size
8889      */
8890     getMinimumSize : function(){
8891         return this.minSize;
8892     },
8893     
8894     /**
8895      * Sets the minimum size for the resizing element
8896      * @param {Number} minSize The minimum size
8897      */
8898     setMinimumSize : function(minSize){
8899         this.minSize = minSize;
8900     },
8901     
8902     /**
8903      * Gets the maximum size for the resizing element
8904      * @return {Number} The maximum size
8905      */
8906     getMaximumSize : function(){
8907         return this.maxSize;
8908     },
8909     
8910     /**
8911      * Sets the maximum size for the resizing element
8912      * @param {Number} maxSize The maximum size
8913      */
8914     setMaximumSize : function(maxSize){
8915         this.maxSize = maxSize;
8916     },
8917     
8918     /**
8919      * Sets the initialize size for the resizing element
8920      * @param {Number} size The initial size
8921      */
8922     setCurrentSize : function(size){
8923         var oldAnimate = this.animate;
8924         this.animate = false;
8925         this.adapter.setElementSize(this, size);
8926         this.animate = oldAnimate;
8927     },
8928     
8929     /**
8930      * Destroy this splitbar. 
8931      * @param {Boolean} removeEl True to remove the element
8932      */
8933     destroy : function(removeEl){
8934         if(this.shim){
8935             this.shim.remove();
8936         }
8937         this.dd.unreg();
8938         this.proxy.parentNode.removeChild(this.proxy);
8939         if(removeEl){
8940             this.el.remove();
8941         }
8942     }
8943 });
8944
8945 /**
8946  * @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.
8947  */
8948 Roo.SplitBar.createProxy = function(dir){
8949     var proxy = new Roo.Element(document.createElement("div"));
8950     proxy.unselectable();
8951     var cls = 'x-splitbar-proxy';
8952     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
8953     document.body.appendChild(proxy.dom);
8954     return proxy.dom;
8955 };
8956
8957 /** 
8958  * @class Roo.SplitBar.BasicLayoutAdapter
8959  * Default Adapter. It assumes the splitter and resizing element are not positioned
8960  * elements and only gets/sets the width of the element. Generally used for table based layouts.
8961  */
8962 Roo.SplitBar.BasicLayoutAdapter = function(){
8963 };
8964
8965 Roo.SplitBar.BasicLayoutAdapter.prototype = {
8966     // do nothing for now
8967     init : function(s){
8968     
8969     },
8970     /**
8971      * Called before drag operations to get the current size of the resizing element. 
8972      * @param {Roo.SplitBar} s The SplitBar using this adapter
8973      */
8974      getElementSize : function(s){
8975         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8976             return s.resizingEl.getWidth();
8977         }else{
8978             return s.resizingEl.getHeight();
8979         }
8980     },
8981     
8982     /**
8983      * Called after drag operations to set the size of the resizing element.
8984      * @param {Roo.SplitBar} s The SplitBar using this adapter
8985      * @param {Number} newSize The new size to set
8986      * @param {Function} onComplete A function to be invoked when resizing is complete
8987      */
8988     setElementSize : function(s, newSize, onComplete){
8989         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8990             if(!s.animate){
8991                 s.resizingEl.setWidth(newSize);
8992                 if(onComplete){
8993                     onComplete(s, newSize);
8994                 }
8995             }else{
8996                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
8997             }
8998         }else{
8999             
9000             if(!s.animate){
9001                 s.resizingEl.setHeight(newSize);
9002                 if(onComplete){
9003                     onComplete(s, newSize);
9004                 }
9005             }else{
9006                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
9007             }
9008         }
9009     }
9010 };
9011
9012 /** 
9013  *@class Roo.SplitBar.AbsoluteLayoutAdapter
9014  * @extends Roo.SplitBar.BasicLayoutAdapter
9015  * Adapter that  moves the splitter element to align with the resized sizing element. 
9016  * Used with an absolute positioned SplitBar.
9017  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
9018  * document.body, make sure you assign an id to the body element.
9019  */
9020 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
9021     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
9022     this.container = Roo.get(container);
9023 };
9024
9025 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
9026     init : function(s){
9027         this.basic.init(s);
9028     },
9029     
9030     getElementSize : function(s){
9031         return this.basic.getElementSize(s);
9032     },
9033     
9034     setElementSize : function(s, newSize, onComplete){
9035         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
9036     },
9037     
9038     moveSplitter : function(s){
9039         var yes = Roo.SplitBar;
9040         switch(s.placement){
9041             case yes.LEFT:
9042                 s.el.setX(s.resizingEl.getRight());
9043                 break;
9044             case yes.RIGHT:
9045                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
9046                 break;
9047             case yes.TOP:
9048                 s.el.setY(s.resizingEl.getBottom());
9049                 break;
9050             case yes.BOTTOM:
9051                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
9052                 break;
9053         }
9054     }
9055 };
9056
9057 /**
9058  * Orientation constant - Create a vertical SplitBar
9059  * @static
9060  * @type Number
9061  */
9062 Roo.SplitBar.VERTICAL = 1;
9063
9064 /**
9065  * Orientation constant - Create a horizontal SplitBar
9066  * @static
9067  * @type Number
9068  */
9069 Roo.SplitBar.HORIZONTAL = 2;
9070
9071 /**
9072  * Placement constant - The resizing element is to the left of the splitter element
9073  * @static
9074  * @type Number
9075  */
9076 Roo.SplitBar.LEFT = 1;
9077
9078 /**
9079  * Placement constant - The resizing element is to the right of the splitter element
9080  * @static
9081  * @type Number
9082  */
9083 Roo.SplitBar.RIGHT = 2;
9084
9085 /**
9086  * Placement constant - The resizing element is positioned above the splitter element
9087  * @static
9088  * @type Number
9089  */
9090 Roo.SplitBar.TOP = 3;
9091
9092 /**
9093  * Placement constant - The resizing element is positioned under splitter element
9094  * @static
9095  * @type Number
9096  */
9097 Roo.SplitBar.BOTTOM = 4;
9098 /*
9099  * Based on:
9100  * Ext JS Library 1.1.1
9101  * Copyright(c) 2006-2007, Ext JS, LLC.
9102  *
9103  * Originally Released Under LGPL - original licence link has changed is not relivant.
9104  *
9105  * Fork - LGPL
9106  * <script type="text/javascript">
9107  */
9108
9109 /**
9110  * @class Roo.View
9111  * @extends Roo.util.Observable
9112  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
9113  * This class also supports single and multi selection modes. <br>
9114  * Create a data model bound view:
9115  <pre><code>
9116  var store = new Roo.data.Store(...);
9117
9118  var view = new Roo.View({
9119     el : "my-element",
9120     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
9121  
9122     singleSelect: true,
9123     selectedClass: "ydataview-selected",
9124     store: store
9125  });
9126
9127  // listen for node click?
9128  view.on("click", function(vw, index, node, e){
9129  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9130  });
9131
9132  // load XML data
9133  dataModel.load("foobar.xml");
9134  </code></pre>
9135  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9136  * <br><br>
9137  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9138  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9139  * 
9140  * Note: old style constructor is still suported (container, template, config)
9141  * 
9142  * @constructor
9143  * Create a new View
9144  * @param {Object} config The config object
9145  * 
9146  */
9147 Roo.View = function(config, depreciated_tpl, depreciated_config){
9148     
9149     if (typeof(depreciated_tpl) == 'undefined') {
9150         // new way.. - universal constructor.
9151         Roo.apply(this, config);
9152         this.el  = Roo.get(this.el);
9153     } else {
9154         // old format..
9155         this.el  = Roo.get(config);
9156         this.tpl = depreciated_tpl;
9157         Roo.apply(this, depreciated_config);
9158     }
9159      
9160     
9161     if(typeof(this.tpl) == "string"){
9162         this.tpl = new Roo.Template(this.tpl);
9163     } else {
9164         // support xtype ctors..
9165         this.tpl = new Roo.factory(this.tpl, Roo);
9166     }
9167     
9168     
9169     this.tpl.compile();
9170    
9171
9172      
9173     /** @private */
9174     this.addEvents({
9175         /**
9176          * @event beforeclick
9177          * Fires before a click is processed. Returns false to cancel the default action.
9178          * @param {Roo.View} this
9179          * @param {Number} index The index of the target node
9180          * @param {HTMLElement} node The target node
9181          * @param {Roo.EventObject} e The raw event object
9182          */
9183             "beforeclick" : true,
9184         /**
9185          * @event click
9186          * Fires when a template node is clicked.
9187          * @param {Roo.View} this
9188          * @param {Number} index The index of the target node
9189          * @param {HTMLElement} node The target node
9190          * @param {Roo.EventObject} e The raw event object
9191          */
9192             "click" : true,
9193         /**
9194          * @event dblclick
9195          * Fires when a template node is double clicked.
9196          * @param {Roo.View} this
9197          * @param {Number} index The index of the target node
9198          * @param {HTMLElement} node The target node
9199          * @param {Roo.EventObject} e The raw event object
9200          */
9201             "dblclick" : true,
9202         /**
9203          * @event contextmenu
9204          * Fires when a template node is right clicked.
9205          * @param {Roo.View} this
9206          * @param {Number} index The index of the target node
9207          * @param {HTMLElement} node The target node
9208          * @param {Roo.EventObject} e The raw event object
9209          */
9210             "contextmenu" : true,
9211         /**
9212          * @event selectionchange
9213          * Fires when the selected nodes change.
9214          * @param {Roo.View} this
9215          * @param {Array} selections Array of the selected nodes
9216          */
9217             "selectionchange" : true,
9218     
9219         /**
9220          * @event beforeselect
9221          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9222          * @param {Roo.View} this
9223          * @param {HTMLElement} node The node to be selected
9224          * @param {Array} selections Array of currently selected nodes
9225          */
9226             "beforeselect" : true,
9227         /**
9228          * @event preparedata
9229          * Fires on every row to render, to allow you to change the data.
9230          * @param {Roo.View} this
9231          * @param {Object} data to be rendered (change this)
9232          */
9233           "preparedata" : true
9234         });
9235
9236     this.el.on({
9237         "click": this.onClick,
9238         "dblclick": this.onDblClick,
9239         "contextmenu": this.onContextMenu,
9240         scope:this
9241     });
9242
9243     this.selections = [];
9244     this.nodes = [];
9245     this.cmp = new Roo.CompositeElementLite([]);
9246     if(this.store){
9247         this.store = Roo.factory(this.store, Roo.data);
9248         this.setStore(this.store, true);
9249     }
9250     Roo.View.superclass.constructor.call(this);
9251 };
9252
9253 Roo.extend(Roo.View, Roo.util.Observable, {
9254     
9255      /**
9256      * @cfg {Roo.data.Store} store Data store to load data from.
9257      */
9258     store : false,
9259     
9260     /**
9261      * @cfg {String|Roo.Element} el The container element.
9262      */
9263     el : '',
9264     
9265     /**
9266      * @cfg {String|Roo.Template} tpl The template used by this View 
9267      */
9268     tpl : false,
9269     /**
9270      * @cfg {String} dataName the named area of the template to use as the data area
9271      *                          Works with domtemplates roo-name="name"
9272      */
9273     dataName: false,
9274     /**
9275      * @cfg {String} selectedClass The css class to add to selected nodes
9276      */
9277     selectedClass : "x-view-selected",
9278      /**
9279      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9280      */
9281     emptyText : "",
9282     /**
9283      * @cfg {Boolean} multiSelect Allow multiple selection
9284      */
9285     multiSelect : false,
9286     /**
9287      * @cfg {Boolean} singleSelect Allow single selection
9288      */
9289     singleSelect:  false,
9290     
9291     /**
9292      * @cfg {Boolean} toggleSelect - selecting 
9293      */
9294     toggleSelect : false,
9295     
9296     /**
9297      * Returns the element this view is bound to.
9298      * @return {Roo.Element}
9299      */
9300     getEl : function(){
9301         return this.el;
9302     },
9303
9304     /**
9305      * Refreshes the view.
9306      */
9307     refresh : function(){
9308         var t = this.tpl;
9309         
9310         // if we are using something like 'domtemplate', then
9311         // the what gets used is:
9312         // t.applySubtemplate(NAME, data, wrapping data..)
9313         // the outer template then get' applied with
9314         //     the store 'extra data'
9315         // and the body get's added to the
9316         //      roo-name="data" node?
9317         //      <span class='roo-tpl-{name}'></span> ?????
9318         
9319         
9320         
9321         this.clearSelections();
9322         this.el.update("");
9323         var html = [];
9324         var records = this.store.getRange();
9325         if(records.length < 1) {
9326             
9327             // is this valid??  = should it render a template??
9328             
9329             this.el.update(this.emptyText);
9330             return;
9331         }
9332         var el = this.el;
9333         if (this.dataName) {
9334             this.el.update(t.apply(this.store.meta)); //????
9335             el = this.el.child('.roo-tpl-' + this.dataName);
9336         }
9337         
9338         for(var i = 0, len = records.length; i < len; i++){
9339             var data = this.prepareData(records[i].data, i, records[i]);
9340             this.fireEvent("preparedata", this, data, i, records[i]);
9341             html[html.length] = Roo.util.Format.trim(
9342                 this.dataName ?
9343                     t.applySubtemplate(this.dataName, data, this.store.meta) :
9344                     t.apply(data)
9345             );
9346         }
9347         
9348         
9349         
9350         el.update(html.join(""));
9351         this.nodes = el.dom.childNodes;
9352         this.updateIndexes(0);
9353     },
9354
9355     /**
9356      * Function to override to reformat the data that is sent to
9357      * the template for each node.
9358      * DEPRICATED - use the preparedata event handler.
9359      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9360      * a JSON object for an UpdateManager bound view).
9361      */
9362     prepareData : function(data, index, record)
9363     {
9364         this.fireEvent("preparedata", this, data, index, record);
9365         return data;
9366     },
9367
9368     onUpdate : function(ds, record){
9369         this.clearSelections();
9370         var index = this.store.indexOf(record);
9371         var n = this.nodes[index];
9372         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
9373         n.parentNode.removeChild(n);
9374         this.updateIndexes(index, index);
9375     },
9376
9377     
9378     
9379 // --------- FIXME     
9380     onAdd : function(ds, records, index)
9381     {
9382         this.clearSelections();
9383         if(this.nodes.length == 0){
9384             this.refresh();
9385             return;
9386         }
9387         var n = this.nodes[index];
9388         for(var i = 0, len = records.length; i < len; i++){
9389             var d = this.prepareData(records[i].data, i, records[i]);
9390             if(n){
9391                 this.tpl.insertBefore(n, d);
9392             }else{
9393                 
9394                 this.tpl.append(this.el, d);
9395             }
9396         }
9397         this.updateIndexes(index);
9398     },
9399
9400     onRemove : function(ds, record, index){
9401         this.clearSelections();
9402         var el = this.dataName  ?
9403             this.el.child('.roo-tpl-' + this.dataName) :
9404             this.el; 
9405         el.dom.removeChild(this.nodes[index]);
9406         this.updateIndexes(index);
9407     },
9408
9409     /**
9410      * Refresh an individual node.
9411      * @param {Number} index
9412      */
9413     refreshNode : function(index){
9414         this.onUpdate(this.store, this.store.getAt(index));
9415     },
9416
9417     updateIndexes : function(startIndex, endIndex){
9418         var ns = this.nodes;
9419         startIndex = startIndex || 0;
9420         endIndex = endIndex || ns.length - 1;
9421         for(var i = startIndex; i <= endIndex; i++){
9422             ns[i].nodeIndex = i;
9423         }
9424     },
9425
9426     /**
9427      * Changes the data store this view uses and refresh the view.
9428      * @param {Store} store
9429      */
9430     setStore : function(store, initial){
9431         if(!initial && this.store){
9432             this.store.un("datachanged", this.refresh);
9433             this.store.un("add", this.onAdd);
9434             this.store.un("remove", this.onRemove);
9435             this.store.un("update", this.onUpdate);
9436             this.store.un("clear", this.refresh);
9437         }
9438         if(store){
9439           
9440             store.on("datachanged", this.refresh, this);
9441             store.on("add", this.onAdd, this);
9442             store.on("remove", this.onRemove, this);
9443             store.on("update", this.onUpdate, this);
9444             store.on("clear", this.refresh, this);
9445         }
9446         
9447         if(store){
9448             this.refresh();
9449         }
9450     },
9451
9452     /**
9453      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9454      * @param {HTMLElement} node
9455      * @return {HTMLElement} The template node
9456      */
9457     findItemFromChild : function(node){
9458         var el = this.dataName  ?
9459             this.el.child('.roo-tpl-' + this.dataName,true) :
9460             this.el.dom; 
9461         
9462         if(!node || node.parentNode == el){
9463                     return node;
9464             }
9465             var p = node.parentNode;
9466             while(p && p != el){
9467             if(p.parentNode == el){
9468                 return p;
9469             }
9470             p = p.parentNode;
9471         }
9472             return null;
9473     },
9474
9475     /** @ignore */
9476     onClick : function(e){
9477         var item = this.findItemFromChild(e.getTarget());
9478         if(item){
9479             var index = this.indexOf(item);
9480             if(this.onItemClick(item, index, e) !== false){
9481                 this.fireEvent("click", this, index, item, e);
9482             }
9483         }else{
9484             this.clearSelections();
9485         }
9486     },
9487
9488     /** @ignore */
9489     onContextMenu : function(e){
9490         var item = this.findItemFromChild(e.getTarget());
9491         if(item){
9492             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9493         }
9494     },
9495
9496     /** @ignore */
9497     onDblClick : function(e){
9498         var item = this.findItemFromChild(e.getTarget());
9499         if(item){
9500             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9501         }
9502     },
9503
9504     onItemClick : function(item, index, e)
9505     {
9506         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9507             return false;
9508         }
9509         if (this.toggleSelect) {
9510             var m = this.isSelected(item) ? 'unselect' : 'select';
9511             Roo.log(m);
9512             var _t = this;
9513             _t[m](item, true, false);
9514             return true;
9515         }
9516         if(this.multiSelect || this.singleSelect){
9517             if(this.multiSelect && e.shiftKey && this.lastSelection){
9518                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9519             }else{
9520                 this.select(item, this.multiSelect && e.ctrlKey);
9521                 this.lastSelection = item;
9522             }
9523             e.preventDefault();
9524         }
9525         return true;
9526     },
9527
9528     /**
9529      * Get the number of selected nodes.
9530      * @return {Number}
9531      */
9532     getSelectionCount : function(){
9533         return this.selections.length;
9534     },
9535
9536     /**
9537      * Get the currently selected nodes.
9538      * @return {Array} An array of HTMLElements
9539      */
9540     getSelectedNodes : function(){
9541         return this.selections;
9542     },
9543
9544     /**
9545      * Get the indexes of the selected nodes.
9546      * @return {Array}
9547      */
9548     getSelectedIndexes : function(){
9549         var indexes = [], s = this.selections;
9550         for(var i = 0, len = s.length; i < len; i++){
9551             indexes.push(s[i].nodeIndex);
9552         }
9553         return indexes;
9554     },
9555
9556     /**
9557      * Clear all selections
9558      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9559      */
9560     clearSelections : function(suppressEvent){
9561         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9562             this.cmp.elements = this.selections;
9563             this.cmp.removeClass(this.selectedClass);
9564             this.selections = [];
9565             if(!suppressEvent){
9566                 this.fireEvent("selectionchange", this, this.selections);
9567             }
9568         }
9569     },
9570
9571     /**
9572      * Returns true if the passed node is selected
9573      * @param {HTMLElement/Number} node The node or node index
9574      * @return {Boolean}
9575      */
9576     isSelected : function(node){
9577         var s = this.selections;
9578         if(s.length < 1){
9579             return false;
9580         }
9581         node = this.getNode(node);
9582         return s.indexOf(node) !== -1;
9583     },
9584
9585     /**
9586      * Selects nodes.
9587      * @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
9588      * @param {Boolean} keepExisting (optional) true to keep existing selections
9589      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9590      */
9591     select : function(nodeInfo, keepExisting, suppressEvent){
9592         if(nodeInfo instanceof Array){
9593             if(!keepExisting){
9594                 this.clearSelections(true);
9595             }
9596             for(var i = 0, len = nodeInfo.length; i < len; i++){
9597                 this.select(nodeInfo[i], true, true);
9598             }
9599             return;
9600         } 
9601         var node = this.getNode(nodeInfo);
9602         if(!node || this.isSelected(node)){
9603             return; // already selected.
9604         }
9605         if(!keepExisting){
9606             this.clearSelections(true);
9607         }
9608         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9609             Roo.fly(node).addClass(this.selectedClass);
9610             this.selections.push(node);
9611             if(!suppressEvent){
9612                 this.fireEvent("selectionchange", this, this.selections);
9613             }
9614         }
9615         
9616         
9617     },
9618       /**
9619      * Unselects nodes.
9620      * @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
9621      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
9622      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9623      */
9624     unselect : function(nodeInfo, keepExisting, suppressEvent)
9625     {
9626         if(nodeInfo instanceof Array){
9627             Roo.each(this.selections, function(s) {
9628                 this.unselect(s, nodeInfo);
9629             }, this);
9630             return;
9631         }
9632         var node = this.getNode(nodeInfo);
9633         if(!node || !this.isSelected(node)){
9634             Roo.log("not selected");
9635             return; // not selected.
9636         }
9637         // fireevent???
9638         var ns = [];
9639         Roo.each(this.selections, function(s) {
9640             if (s == node ) {
9641                 Roo.fly(node).removeClass(this.selectedClass);
9642
9643                 return;
9644             }
9645             ns.push(s);
9646         },this);
9647         
9648         this.selections= ns;
9649         this.fireEvent("selectionchange", this, this.selections);
9650     },
9651
9652     /**
9653      * Gets a template node.
9654      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9655      * @return {HTMLElement} The node or null if it wasn't found
9656      */
9657     getNode : function(nodeInfo){
9658         if(typeof nodeInfo == "string"){
9659             return document.getElementById(nodeInfo);
9660         }else if(typeof nodeInfo == "number"){
9661             return this.nodes[nodeInfo];
9662         }
9663         return nodeInfo;
9664     },
9665
9666     /**
9667      * Gets a range template nodes.
9668      * @param {Number} startIndex
9669      * @param {Number} endIndex
9670      * @return {Array} An array of nodes
9671      */
9672     getNodes : function(start, end){
9673         var ns = this.nodes;
9674         start = start || 0;
9675         end = typeof end == "undefined" ? ns.length - 1 : end;
9676         var nodes = [];
9677         if(start <= end){
9678             for(var i = start; i <= end; i++){
9679                 nodes.push(ns[i]);
9680             }
9681         } else{
9682             for(var i = start; i >= end; i--){
9683                 nodes.push(ns[i]);
9684             }
9685         }
9686         return nodes;
9687     },
9688
9689     /**
9690      * Finds the index of the passed node
9691      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9692      * @return {Number} The index of the node or -1
9693      */
9694     indexOf : function(node){
9695         node = this.getNode(node);
9696         if(typeof node.nodeIndex == "number"){
9697             return node.nodeIndex;
9698         }
9699         var ns = this.nodes;
9700         for(var i = 0, len = ns.length; i < len; i++){
9701             if(ns[i] == node){
9702                 return i;
9703             }
9704         }
9705         return -1;
9706     }
9707 });
9708 /*
9709  * Based on:
9710  * Ext JS Library 1.1.1
9711  * Copyright(c) 2006-2007, Ext JS, LLC.
9712  *
9713  * Originally Released Under LGPL - original licence link has changed is not relivant.
9714  *
9715  * Fork - LGPL
9716  * <script type="text/javascript">
9717  */
9718
9719 /**
9720  * @class Roo.JsonView
9721  * @extends Roo.View
9722  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9723 <pre><code>
9724 var view = new Roo.JsonView({
9725     container: "my-element",
9726     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
9727     multiSelect: true, 
9728     jsonRoot: "data" 
9729 });
9730
9731 // listen for node click?
9732 view.on("click", function(vw, index, node, e){
9733     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9734 });
9735
9736 // direct load of JSON data
9737 view.load("foobar.php");
9738
9739 // Example from my blog list
9740 var tpl = new Roo.Template(
9741     '&lt;div class="entry"&gt;' +
9742     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
9743     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
9744     "&lt;/div&gt;&lt;hr /&gt;"
9745 );
9746
9747 var moreView = new Roo.JsonView({
9748     container :  "entry-list", 
9749     template : tpl,
9750     jsonRoot: "posts"
9751 });
9752 moreView.on("beforerender", this.sortEntries, this);
9753 moreView.load({
9754     url: "/blog/get-posts.php",
9755     params: "allposts=true",
9756     text: "Loading Blog Entries..."
9757 });
9758 </code></pre>
9759
9760 * Note: old code is supported with arguments : (container, template, config)
9761
9762
9763  * @constructor
9764  * Create a new JsonView
9765  * 
9766  * @param {Object} config The config object
9767  * 
9768  */
9769 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9770     
9771     
9772     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9773
9774     var um = this.el.getUpdateManager();
9775     um.setRenderer(this);
9776     um.on("update", this.onLoad, this);
9777     um.on("failure", this.onLoadException, this);
9778
9779     /**
9780      * @event beforerender
9781      * Fires before rendering of the downloaded JSON data.
9782      * @param {Roo.JsonView} this
9783      * @param {Object} data The JSON data loaded
9784      */
9785     /**
9786      * @event load
9787      * Fires when data is loaded.
9788      * @param {Roo.JsonView} this
9789      * @param {Object} data The JSON data loaded
9790      * @param {Object} response The raw Connect response object
9791      */
9792     /**
9793      * @event loadexception
9794      * Fires when loading fails.
9795      * @param {Roo.JsonView} this
9796      * @param {Object} response The raw Connect response object
9797      */
9798     this.addEvents({
9799         'beforerender' : true,
9800         'load' : true,
9801         'loadexception' : true
9802     });
9803 };
9804 Roo.extend(Roo.JsonView, Roo.View, {
9805     /**
9806      * @type {String} The root property in the loaded JSON object that contains the data
9807      */
9808     jsonRoot : "",
9809
9810     /**
9811      * Refreshes the view.
9812      */
9813     refresh : function(){
9814         this.clearSelections();
9815         this.el.update("");
9816         var html = [];
9817         var o = this.jsonData;
9818         if(o && o.length > 0){
9819             for(var i = 0, len = o.length; i < len; i++){
9820                 var data = this.prepareData(o[i], i, o);
9821                 html[html.length] = this.tpl.apply(data);
9822             }
9823         }else{
9824             html.push(this.emptyText);
9825         }
9826         this.el.update(html.join(""));
9827         this.nodes = this.el.dom.childNodes;
9828         this.updateIndexes(0);
9829     },
9830
9831     /**
9832      * 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.
9833      * @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:
9834      <pre><code>
9835      view.load({
9836          url: "your-url.php",
9837          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9838          callback: yourFunction,
9839          scope: yourObject, //(optional scope)
9840          discardUrl: false,
9841          nocache: false,
9842          text: "Loading...",
9843          timeout: 30,
9844          scripts: false
9845      });
9846      </code></pre>
9847      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9848      * 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.
9849      * @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}
9850      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9851      * @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.
9852      */
9853     load : function(){
9854         var um = this.el.getUpdateManager();
9855         um.update.apply(um, arguments);
9856     },
9857
9858     render : function(el, response){
9859         this.clearSelections();
9860         this.el.update("");
9861         var o;
9862         try{
9863             o = Roo.util.JSON.decode(response.responseText);
9864             if(this.jsonRoot){
9865                 
9866                 o = o[this.jsonRoot];
9867             }
9868         } catch(e){
9869         }
9870         /**
9871          * The current JSON data or null
9872          */
9873         this.jsonData = o;
9874         this.beforeRender();
9875         this.refresh();
9876     },
9877
9878 /**
9879  * Get the number of records in the current JSON dataset
9880  * @return {Number}
9881  */
9882     getCount : function(){
9883         return this.jsonData ? this.jsonData.length : 0;
9884     },
9885
9886 /**
9887  * Returns the JSON object for the specified node(s)
9888  * @param {HTMLElement/Array} node The node or an array of nodes
9889  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9890  * you get the JSON object for the node
9891  */
9892     getNodeData : function(node){
9893         if(node instanceof Array){
9894             var data = [];
9895             for(var i = 0, len = node.length; i < len; i++){
9896                 data.push(this.getNodeData(node[i]));
9897             }
9898             return data;
9899         }
9900         return this.jsonData[this.indexOf(node)] || null;
9901     },
9902
9903     beforeRender : function(){
9904         this.snapshot = this.jsonData;
9905         if(this.sortInfo){
9906             this.sort.apply(this, this.sortInfo);
9907         }
9908         this.fireEvent("beforerender", this, this.jsonData);
9909     },
9910
9911     onLoad : function(el, o){
9912         this.fireEvent("load", this, this.jsonData, o);
9913     },
9914
9915     onLoadException : function(el, o){
9916         this.fireEvent("loadexception", this, o);
9917     },
9918
9919 /**
9920  * Filter the data by a specific property.
9921  * @param {String} property A property on your JSON objects
9922  * @param {String/RegExp} value Either string that the property values
9923  * should start with, or a RegExp to test against the property
9924  */
9925     filter : function(property, value){
9926         if(this.jsonData){
9927             var data = [];
9928             var ss = this.snapshot;
9929             if(typeof value == "string"){
9930                 var vlen = value.length;
9931                 if(vlen == 0){
9932                     this.clearFilter();
9933                     return;
9934                 }
9935                 value = value.toLowerCase();
9936                 for(var i = 0, len = ss.length; i < len; i++){
9937                     var o = ss[i];
9938                     if(o[property].substr(0, vlen).toLowerCase() == value){
9939                         data.push(o);
9940                     }
9941                 }
9942             } else if(value.exec){ // regex?
9943                 for(var i = 0, len = ss.length; i < len; i++){
9944                     var o = ss[i];
9945                     if(value.test(o[property])){
9946                         data.push(o);
9947                     }
9948                 }
9949             } else{
9950                 return;
9951             }
9952             this.jsonData = data;
9953             this.refresh();
9954         }
9955     },
9956
9957 /**
9958  * Filter by a function. The passed function will be called with each
9959  * object in the current dataset. If the function returns true the value is kept,
9960  * otherwise it is filtered.
9961  * @param {Function} fn
9962  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
9963  */
9964     filterBy : function(fn, scope){
9965         if(this.jsonData){
9966             var data = [];
9967             var ss = this.snapshot;
9968             for(var i = 0, len = ss.length; i < len; i++){
9969                 var o = ss[i];
9970                 if(fn.call(scope || this, o)){
9971                     data.push(o);
9972                 }
9973             }
9974             this.jsonData = data;
9975             this.refresh();
9976         }
9977     },
9978
9979 /**
9980  * Clears the current filter.
9981  */
9982     clearFilter : function(){
9983         if(this.snapshot && this.jsonData != this.snapshot){
9984             this.jsonData = this.snapshot;
9985             this.refresh();
9986         }
9987     },
9988
9989
9990 /**
9991  * Sorts the data for this view and refreshes it.
9992  * @param {String} property A property on your JSON objects to sort on
9993  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
9994  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
9995  */
9996     sort : function(property, dir, sortType){
9997         this.sortInfo = Array.prototype.slice.call(arguments, 0);
9998         if(this.jsonData){
9999             var p = property;
10000             var dsc = dir && dir.toLowerCase() == "desc";
10001             var f = function(o1, o2){
10002                 var v1 = sortType ? sortType(o1[p]) : o1[p];
10003                 var v2 = sortType ? sortType(o2[p]) : o2[p];
10004                 ;
10005                 if(v1 < v2){
10006                     return dsc ? +1 : -1;
10007                 } else if(v1 > v2){
10008                     return dsc ? -1 : +1;
10009                 } else{
10010                     return 0;
10011                 }
10012             };
10013             this.jsonData.sort(f);
10014             this.refresh();
10015             if(this.jsonData != this.snapshot){
10016                 this.snapshot.sort(f);
10017             }
10018         }
10019     }
10020 });/*
10021  * Based on:
10022  * Ext JS Library 1.1.1
10023  * Copyright(c) 2006-2007, Ext JS, LLC.
10024  *
10025  * Originally Released Under LGPL - original licence link has changed is not relivant.
10026  *
10027  * Fork - LGPL
10028  * <script type="text/javascript">
10029  */
10030  
10031
10032 /**
10033  * @class Roo.ColorPalette
10034  * @extends Roo.Component
10035  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
10036  * Here's an example of typical usage:
10037  * <pre><code>
10038 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
10039 cp.render('my-div');
10040
10041 cp.on('select', function(palette, selColor){
10042     // do something with selColor
10043 });
10044 </code></pre>
10045  * @constructor
10046  * Create a new ColorPalette
10047  * @param {Object} config The config object
10048  */
10049 Roo.ColorPalette = function(config){
10050     Roo.ColorPalette.superclass.constructor.call(this, config);
10051     this.addEvents({
10052         /**
10053              * @event select
10054              * Fires when a color is selected
10055              * @param {ColorPalette} this
10056              * @param {String} color The 6-digit color hex code (without the # symbol)
10057              */
10058         select: true
10059     });
10060
10061     if(this.handler){
10062         this.on("select", this.handler, this.scope, true);
10063     }
10064 };
10065 Roo.extend(Roo.ColorPalette, Roo.Component, {
10066     /**
10067      * @cfg {String} itemCls
10068      * The CSS class to apply to the containing element (defaults to "x-color-palette")
10069      */
10070     itemCls : "x-color-palette",
10071     /**
10072      * @cfg {String} value
10073      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
10074      * the hex codes are case-sensitive.
10075      */
10076     value : null,
10077     clickEvent:'click',
10078     // private
10079     ctype: "Roo.ColorPalette",
10080
10081     /**
10082      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
10083      */
10084     allowReselect : false,
10085
10086     /**
10087      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
10088      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
10089      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
10090      * of colors with the width setting until the box is symmetrical.</p>
10091      * <p>You can override individual colors if needed:</p>
10092      * <pre><code>
10093 var cp = new Roo.ColorPalette();
10094 cp.colors[0] = "FF0000";  // change the first box to red
10095 </code></pre>
10096
10097 Or you can provide a custom array of your own for complete control:
10098 <pre><code>
10099 var cp = new Roo.ColorPalette();
10100 cp.colors = ["000000", "993300", "333300"];
10101 </code></pre>
10102      * @type Array
10103      */
10104     colors : [
10105         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
10106         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
10107         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
10108         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
10109         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
10110     ],
10111
10112     // private
10113     onRender : function(container, position){
10114         var t = new Roo.MasterTemplate(
10115             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
10116         );
10117         var c = this.colors;
10118         for(var i = 0, len = c.length; i < len; i++){
10119             t.add([c[i]]);
10120         }
10121         var el = document.createElement("div");
10122         el.className = this.itemCls;
10123         t.overwrite(el);
10124         container.dom.insertBefore(el, position);
10125         this.el = Roo.get(el);
10126         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
10127         if(this.clickEvent != 'click'){
10128             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
10129         }
10130     },
10131
10132     // private
10133     afterRender : function(){
10134         Roo.ColorPalette.superclass.afterRender.call(this);
10135         if(this.value){
10136             var s = this.value;
10137             this.value = null;
10138             this.select(s);
10139         }
10140     },
10141
10142     // private
10143     handleClick : function(e, t){
10144         e.preventDefault();
10145         if(!this.disabled){
10146             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
10147             this.select(c.toUpperCase());
10148         }
10149     },
10150
10151     /**
10152      * Selects the specified color in the palette (fires the select event)
10153      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
10154      */
10155     select : function(color){
10156         color = color.replace("#", "");
10157         if(color != this.value || this.allowReselect){
10158             var el = this.el;
10159             if(this.value){
10160                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
10161             }
10162             el.child("a.color-"+color).addClass("x-color-palette-sel");
10163             this.value = color;
10164             this.fireEvent("select", this, color);
10165         }
10166     }
10167 });/*
10168  * Based on:
10169  * Ext JS Library 1.1.1
10170  * Copyright(c) 2006-2007, Ext JS, LLC.
10171  *
10172  * Originally Released Under LGPL - original licence link has changed is not relivant.
10173  *
10174  * Fork - LGPL
10175  * <script type="text/javascript">
10176  */
10177  
10178 /**
10179  * @class Roo.DatePicker
10180  * @extends Roo.Component
10181  * Simple date picker class.
10182  * @constructor
10183  * Create a new DatePicker
10184  * @param {Object} config The config object
10185  */
10186 Roo.DatePicker = function(config){
10187     Roo.DatePicker.superclass.constructor.call(this, config);
10188
10189     this.value = config && config.value ?
10190                  config.value.clearTime() : new Date().clearTime();
10191
10192     this.addEvents({
10193         /**
10194              * @event select
10195              * Fires when a date is selected
10196              * @param {DatePicker} this
10197              * @param {Date} date The selected date
10198              */
10199         'select': true,
10200         /**
10201              * @event monthchange
10202              * Fires when the displayed month changes 
10203              * @param {DatePicker} this
10204              * @param {Date} date The selected month
10205              */
10206         'monthchange': true
10207     });
10208
10209     if(this.handler){
10210         this.on("select", this.handler,  this.scope || this);
10211     }
10212     // build the disabledDatesRE
10213     if(!this.disabledDatesRE && this.disabledDates){
10214         var dd = this.disabledDates;
10215         var re = "(?:";
10216         for(var i = 0; i < dd.length; i++){
10217             re += dd[i];
10218             if(i != dd.length-1) re += "|";
10219         }
10220         this.disabledDatesRE = new RegExp(re + ")");
10221     }
10222 };
10223
10224 Roo.extend(Roo.DatePicker, Roo.Component, {
10225     /**
10226      * @cfg {String} todayText
10227      * The text to display on the button that selects the current date (defaults to "Today")
10228      */
10229     todayText : "Today",
10230     /**
10231      * @cfg {String} okText
10232      * The text to display on the ok button
10233      */
10234     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
10235     /**
10236      * @cfg {String} cancelText
10237      * The text to display on the cancel button
10238      */
10239     cancelText : "Cancel",
10240     /**
10241      * @cfg {String} todayTip
10242      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
10243      */
10244     todayTip : "{0} (Spacebar)",
10245     /**
10246      * @cfg {Date} minDate
10247      * Minimum allowable date (JavaScript date object, defaults to null)
10248      */
10249     minDate : null,
10250     /**
10251      * @cfg {Date} maxDate
10252      * Maximum allowable date (JavaScript date object, defaults to null)
10253      */
10254     maxDate : null,
10255     /**
10256      * @cfg {String} minText
10257      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
10258      */
10259     minText : "This date is before the minimum date",
10260     /**
10261      * @cfg {String} maxText
10262      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
10263      */
10264     maxText : "This date is after the maximum date",
10265     /**
10266      * @cfg {String} format
10267      * The default date format string which can be overriden for localization support.  The format must be
10268      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
10269      */
10270     format : "m/d/y",
10271     /**
10272      * @cfg {Array} disabledDays
10273      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
10274      */
10275     disabledDays : null,
10276     /**
10277      * @cfg {String} disabledDaysText
10278      * The tooltip to display when the date falls on a disabled day (defaults to "")
10279      */
10280     disabledDaysText : "",
10281     /**
10282      * @cfg {RegExp} disabledDatesRE
10283      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
10284      */
10285     disabledDatesRE : null,
10286     /**
10287      * @cfg {String} disabledDatesText
10288      * The tooltip text to display when the date falls on a disabled date (defaults to "")
10289      */
10290     disabledDatesText : "",
10291     /**
10292      * @cfg {Boolean} constrainToViewport
10293      * True to constrain the date picker to the viewport (defaults to true)
10294      */
10295     constrainToViewport : true,
10296     /**
10297      * @cfg {Array} monthNames
10298      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
10299      */
10300     monthNames : Date.monthNames,
10301     /**
10302      * @cfg {Array} dayNames
10303      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
10304      */
10305     dayNames : Date.dayNames,
10306     /**
10307      * @cfg {String} nextText
10308      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
10309      */
10310     nextText: 'Next Month (Control+Right)',
10311     /**
10312      * @cfg {String} prevText
10313      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
10314      */
10315     prevText: 'Previous Month (Control+Left)',
10316     /**
10317      * @cfg {String} monthYearText
10318      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
10319      */
10320     monthYearText: 'Choose a month (Control+Up/Down to move years)',
10321     /**
10322      * @cfg {Number} startDay
10323      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
10324      */
10325     startDay : 0,
10326     /**
10327      * @cfg {Bool} showClear
10328      * Show a clear button (usefull for date form elements that can be blank.)
10329      */
10330     
10331     showClear: false,
10332     
10333     /**
10334      * Sets the value of the date field
10335      * @param {Date} value The date to set
10336      */
10337     setValue : function(value){
10338         var old = this.value;
10339         if (typeof(value) == 'string') {
10340             value = Date.parseDate(value, this.format);
10341         }
10342         
10343         this.value = value.clearTime(true);
10344         if(this.el){
10345             this.update(this.value);
10346         }
10347     },
10348
10349     /**
10350      * Gets the current selected value of the date field
10351      * @return {Date} The selected date
10352      */
10353     getValue : function(){
10354         return this.value;
10355     },
10356
10357     // private
10358     focus : function(){
10359         if(this.el){
10360             this.update(this.activeDate);
10361         }
10362     },
10363
10364     // private
10365     onRender : function(container, position){
10366         
10367         var m = [
10368              '<table cellspacing="0">',
10369                 '<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>',
10370                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
10371         var dn = this.dayNames;
10372         for(var i = 0; i < 7; i++){
10373             var d = this.startDay+i;
10374             if(d > 6){
10375                 d = d-7;
10376             }
10377             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
10378         }
10379         m[m.length] = "</tr></thead><tbody><tr>";
10380         for(var i = 0; i < 42; i++) {
10381             if(i % 7 == 0 && i != 0){
10382                 m[m.length] = "</tr><tr>";
10383             }
10384             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
10385         }
10386         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
10387             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
10388
10389         var el = document.createElement("div");
10390         el.className = "x-date-picker";
10391         el.innerHTML = m.join("");
10392
10393         container.dom.insertBefore(el, position);
10394
10395         this.el = Roo.get(el);
10396         this.eventEl = Roo.get(el.firstChild);
10397
10398         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
10399             handler: this.showPrevMonth,
10400             scope: this,
10401             preventDefault:true,
10402             stopDefault:true
10403         });
10404
10405         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
10406             handler: this.showNextMonth,
10407             scope: this,
10408             preventDefault:true,
10409             stopDefault:true
10410         });
10411
10412         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
10413
10414         this.monthPicker = this.el.down('div.x-date-mp');
10415         this.monthPicker.enableDisplayMode('block');
10416         
10417         var kn = new Roo.KeyNav(this.eventEl, {
10418             "left" : function(e){
10419                 e.ctrlKey ?
10420                     this.showPrevMonth() :
10421                     this.update(this.activeDate.add("d", -1));
10422             },
10423
10424             "right" : function(e){
10425                 e.ctrlKey ?
10426                     this.showNextMonth() :
10427                     this.update(this.activeDate.add("d", 1));
10428             },
10429
10430             "up" : function(e){
10431                 e.ctrlKey ?
10432                     this.showNextYear() :
10433                     this.update(this.activeDate.add("d", -7));
10434             },
10435
10436             "down" : function(e){
10437                 e.ctrlKey ?
10438                     this.showPrevYear() :
10439                     this.update(this.activeDate.add("d", 7));
10440             },
10441
10442             "pageUp" : function(e){
10443                 this.showNextMonth();
10444             },
10445
10446             "pageDown" : function(e){
10447                 this.showPrevMonth();
10448             },
10449
10450             "enter" : function(e){
10451                 e.stopPropagation();
10452                 return true;
10453             },
10454
10455             scope : this
10456         });
10457
10458         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
10459
10460         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
10461
10462         this.el.unselectable();
10463         
10464         this.cells = this.el.select("table.x-date-inner tbody td");
10465         this.textNodes = this.el.query("table.x-date-inner tbody span");
10466
10467         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
10468             text: "&#160;",
10469             tooltip: this.monthYearText
10470         });
10471
10472         this.mbtn.on('click', this.showMonthPicker, this);
10473         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
10474
10475
10476         var today = (new Date()).dateFormat(this.format);
10477         
10478         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
10479         if (this.showClear) {
10480             baseTb.add( new Roo.Toolbar.Fill());
10481         }
10482         baseTb.add({
10483             text: String.format(this.todayText, today),
10484             tooltip: String.format(this.todayTip, today),
10485             handler: this.selectToday,
10486             scope: this
10487         });
10488         
10489         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
10490             
10491         //});
10492         if (this.showClear) {
10493             
10494             baseTb.add( new Roo.Toolbar.Fill());
10495             baseTb.add({
10496                 text: '&#160;',
10497                 cls: 'x-btn-icon x-btn-clear',
10498                 handler: function() {
10499                     //this.value = '';
10500                     this.fireEvent("select", this, '');
10501                 },
10502                 scope: this
10503             });
10504         }
10505         
10506         
10507         if(Roo.isIE){
10508             this.el.repaint();
10509         }
10510         this.update(this.value);
10511     },
10512
10513     createMonthPicker : function(){
10514         if(!this.monthPicker.dom.firstChild){
10515             var buf = ['<table border="0" cellspacing="0">'];
10516             for(var i = 0; i < 6; i++){
10517                 buf.push(
10518                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
10519                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
10520                     i == 0 ?
10521                     '<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>' :
10522                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
10523                 );
10524             }
10525             buf.push(
10526                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
10527                     this.okText,
10528                     '</button><button type="button" class="x-date-mp-cancel">',
10529                     this.cancelText,
10530                     '</button></td></tr>',
10531                 '</table>'
10532             );
10533             this.monthPicker.update(buf.join(''));
10534             this.monthPicker.on('click', this.onMonthClick, this);
10535             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
10536
10537             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
10538             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
10539
10540             this.mpMonths.each(function(m, a, i){
10541                 i += 1;
10542                 if((i%2) == 0){
10543                     m.dom.xmonth = 5 + Math.round(i * .5);
10544                 }else{
10545                     m.dom.xmonth = Math.round((i-1) * .5);
10546                 }
10547             });
10548         }
10549     },
10550
10551     showMonthPicker : function(){
10552         this.createMonthPicker();
10553         var size = this.el.getSize();
10554         this.monthPicker.setSize(size);
10555         this.monthPicker.child('table').setSize(size);
10556
10557         this.mpSelMonth = (this.activeDate || this.value).getMonth();
10558         this.updateMPMonth(this.mpSelMonth);
10559         this.mpSelYear = (this.activeDate || this.value).getFullYear();
10560         this.updateMPYear(this.mpSelYear);
10561
10562         this.monthPicker.slideIn('t', {duration:.2});
10563     },
10564
10565     updateMPYear : function(y){
10566         this.mpyear = y;
10567         var ys = this.mpYears.elements;
10568         for(var i = 1; i <= 10; i++){
10569             var td = ys[i-1], y2;
10570             if((i%2) == 0){
10571                 y2 = y + Math.round(i * .5);
10572                 td.firstChild.innerHTML = y2;
10573                 td.xyear = y2;
10574             }else{
10575                 y2 = y - (5-Math.round(i * .5));
10576                 td.firstChild.innerHTML = y2;
10577                 td.xyear = y2;
10578             }
10579             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
10580         }
10581     },
10582
10583     updateMPMonth : function(sm){
10584         this.mpMonths.each(function(m, a, i){
10585             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
10586         });
10587     },
10588
10589     selectMPMonth: function(m){
10590         
10591     },
10592
10593     onMonthClick : function(e, t){
10594         e.stopEvent();
10595         var el = new Roo.Element(t), pn;
10596         if(el.is('button.x-date-mp-cancel')){
10597             this.hideMonthPicker();
10598         }
10599         else if(el.is('button.x-date-mp-ok')){
10600             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10601             this.hideMonthPicker();
10602         }
10603         else if(pn = el.up('td.x-date-mp-month', 2)){
10604             this.mpMonths.removeClass('x-date-mp-sel');
10605             pn.addClass('x-date-mp-sel');
10606             this.mpSelMonth = pn.dom.xmonth;
10607         }
10608         else if(pn = el.up('td.x-date-mp-year', 2)){
10609             this.mpYears.removeClass('x-date-mp-sel');
10610             pn.addClass('x-date-mp-sel');
10611             this.mpSelYear = pn.dom.xyear;
10612         }
10613         else if(el.is('a.x-date-mp-prev')){
10614             this.updateMPYear(this.mpyear-10);
10615         }
10616         else if(el.is('a.x-date-mp-next')){
10617             this.updateMPYear(this.mpyear+10);
10618         }
10619     },
10620
10621     onMonthDblClick : function(e, t){
10622         e.stopEvent();
10623         var el = new Roo.Element(t), pn;
10624         if(pn = el.up('td.x-date-mp-month', 2)){
10625             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
10626             this.hideMonthPicker();
10627         }
10628         else if(pn = el.up('td.x-date-mp-year', 2)){
10629             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10630             this.hideMonthPicker();
10631         }
10632     },
10633
10634     hideMonthPicker : function(disableAnim){
10635         if(this.monthPicker){
10636             if(disableAnim === true){
10637                 this.monthPicker.hide();
10638             }else{
10639                 this.monthPicker.slideOut('t', {duration:.2});
10640             }
10641         }
10642     },
10643
10644     // private
10645     showPrevMonth : function(e){
10646         this.update(this.activeDate.add("mo", -1));
10647     },
10648
10649     // private
10650     showNextMonth : function(e){
10651         this.update(this.activeDate.add("mo", 1));
10652     },
10653
10654     // private
10655     showPrevYear : function(){
10656         this.update(this.activeDate.add("y", -1));
10657     },
10658
10659     // private
10660     showNextYear : function(){
10661         this.update(this.activeDate.add("y", 1));
10662     },
10663
10664     // private
10665     handleMouseWheel : function(e){
10666         var delta = e.getWheelDelta();
10667         if(delta > 0){
10668             this.showPrevMonth();
10669             e.stopEvent();
10670         } else if(delta < 0){
10671             this.showNextMonth();
10672             e.stopEvent();
10673         }
10674     },
10675
10676     // private
10677     handleDateClick : function(e, t){
10678         e.stopEvent();
10679         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10680             this.setValue(new Date(t.dateValue));
10681             this.fireEvent("select", this, this.value);
10682         }
10683     },
10684
10685     // private
10686     selectToday : function(){
10687         this.setValue(new Date().clearTime());
10688         this.fireEvent("select", this, this.value);
10689     },
10690
10691     // private
10692     update : function(date)
10693     {
10694         var vd = this.activeDate;
10695         this.activeDate = date;
10696         if(vd && this.el){
10697             var t = date.getTime();
10698             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10699                 this.cells.removeClass("x-date-selected");
10700                 this.cells.each(function(c){
10701                    if(c.dom.firstChild.dateValue == t){
10702                        c.addClass("x-date-selected");
10703                        setTimeout(function(){
10704                             try{c.dom.firstChild.focus();}catch(e){}
10705                        }, 50);
10706                        return false;
10707                    }
10708                 });
10709                 return;
10710             }
10711         }
10712         
10713         var days = date.getDaysInMonth();
10714         var firstOfMonth = date.getFirstDateOfMonth();
10715         var startingPos = firstOfMonth.getDay()-this.startDay;
10716
10717         if(startingPos <= this.startDay){
10718             startingPos += 7;
10719         }
10720
10721         var pm = date.add("mo", -1);
10722         var prevStart = pm.getDaysInMonth()-startingPos;
10723
10724         var cells = this.cells.elements;
10725         var textEls = this.textNodes;
10726         days += startingPos;
10727
10728         // convert everything to numbers so it's fast
10729         var day = 86400000;
10730         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10731         var today = new Date().clearTime().getTime();
10732         var sel = date.clearTime().getTime();
10733         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10734         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10735         var ddMatch = this.disabledDatesRE;
10736         var ddText = this.disabledDatesText;
10737         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10738         var ddaysText = this.disabledDaysText;
10739         var format = this.format;
10740
10741         var setCellClass = function(cal, cell){
10742             cell.title = "";
10743             var t = d.getTime();
10744             cell.firstChild.dateValue = t;
10745             if(t == today){
10746                 cell.className += " x-date-today";
10747                 cell.title = cal.todayText;
10748             }
10749             if(t == sel){
10750                 cell.className += " x-date-selected";
10751                 setTimeout(function(){
10752                     try{cell.firstChild.focus();}catch(e){}
10753                 }, 50);
10754             }
10755             // disabling
10756             if(t < min) {
10757                 cell.className = " x-date-disabled";
10758                 cell.title = cal.minText;
10759                 return;
10760             }
10761             if(t > max) {
10762                 cell.className = " x-date-disabled";
10763                 cell.title = cal.maxText;
10764                 return;
10765             }
10766             if(ddays){
10767                 if(ddays.indexOf(d.getDay()) != -1){
10768                     cell.title = ddaysText;
10769                     cell.className = " x-date-disabled";
10770                 }
10771             }
10772             if(ddMatch && format){
10773                 var fvalue = d.dateFormat(format);
10774                 if(ddMatch.test(fvalue)){
10775                     cell.title = ddText.replace("%0", fvalue);
10776                     cell.className = " x-date-disabled";
10777                 }
10778             }
10779         };
10780
10781         var i = 0;
10782         for(; i < startingPos; i++) {
10783             textEls[i].innerHTML = (++prevStart);
10784             d.setDate(d.getDate()+1);
10785             cells[i].className = "x-date-prevday";
10786             setCellClass(this, cells[i]);
10787         }
10788         for(; i < days; i++){
10789             intDay = i - startingPos + 1;
10790             textEls[i].innerHTML = (intDay);
10791             d.setDate(d.getDate()+1);
10792             cells[i].className = "x-date-active";
10793             setCellClass(this, cells[i]);
10794         }
10795         var extraDays = 0;
10796         for(; i < 42; i++) {
10797              textEls[i].innerHTML = (++extraDays);
10798              d.setDate(d.getDate()+1);
10799              cells[i].className = "x-date-nextday";
10800              setCellClass(this, cells[i]);
10801         }
10802
10803         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10804         this.fireEvent('monthchange', this, date);
10805         
10806         if(!this.internalRender){
10807             var main = this.el.dom.firstChild;
10808             var w = main.offsetWidth;
10809             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10810             Roo.fly(main).setWidth(w);
10811             this.internalRender = true;
10812             // opera does not respect the auto grow header center column
10813             // then, after it gets a width opera refuses to recalculate
10814             // without a second pass
10815             if(Roo.isOpera && !this.secondPass){
10816                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10817                 this.secondPass = true;
10818                 this.update.defer(10, this, [date]);
10819             }
10820         }
10821         
10822         
10823     }
10824 });        /*
10825  * Based on:
10826  * Ext JS Library 1.1.1
10827  * Copyright(c) 2006-2007, Ext JS, LLC.
10828  *
10829  * Originally Released Under LGPL - original licence link has changed is not relivant.
10830  *
10831  * Fork - LGPL
10832  * <script type="text/javascript">
10833  */
10834 /**
10835  * @class Roo.TabPanel
10836  * @extends Roo.util.Observable
10837  * A lightweight tab container.
10838  * <br><br>
10839  * Usage:
10840  * <pre><code>
10841 // basic tabs 1, built from existing content
10842 var tabs = new Roo.TabPanel("tabs1");
10843 tabs.addTab("script", "View Script");
10844 tabs.addTab("markup", "View Markup");
10845 tabs.activate("script");
10846
10847 // more advanced tabs, built from javascript
10848 var jtabs = new Roo.TabPanel("jtabs");
10849 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10850
10851 // set up the UpdateManager
10852 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10853 var updater = tab2.getUpdateManager();
10854 updater.setDefaultUrl("ajax1.htm");
10855 tab2.on('activate', updater.refresh, updater, true);
10856
10857 // Use setUrl for Ajax loading
10858 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10859 tab3.setUrl("ajax2.htm", null, true);
10860
10861 // Disabled tab
10862 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10863 tab4.disable();
10864
10865 jtabs.activate("jtabs-1");
10866  * </code></pre>
10867  * @constructor
10868  * Create a new TabPanel.
10869  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10870  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10871  */
10872 Roo.TabPanel = function(container, config){
10873     /**
10874     * The container element for this TabPanel.
10875     * @type Roo.Element
10876     */
10877     this.el = Roo.get(container, true);
10878     if(config){
10879         if(typeof config == "boolean"){
10880             this.tabPosition = config ? "bottom" : "top";
10881         }else{
10882             Roo.apply(this, config);
10883         }
10884     }
10885     if(this.tabPosition == "bottom"){
10886         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10887         this.el.addClass("x-tabs-bottom");
10888     }
10889     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10890     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10891     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10892     if(Roo.isIE){
10893         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10894     }
10895     if(this.tabPosition != "bottom"){
10896         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
10897          * @type Roo.Element
10898          */
10899         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10900         this.el.addClass("x-tabs-top");
10901     }
10902     this.items = [];
10903
10904     this.bodyEl.setStyle("position", "relative");
10905
10906     this.active = null;
10907     this.activateDelegate = this.activate.createDelegate(this);
10908
10909     this.addEvents({
10910         /**
10911          * @event tabchange
10912          * Fires when the active tab changes
10913          * @param {Roo.TabPanel} this
10914          * @param {Roo.TabPanelItem} activePanel The new active tab
10915          */
10916         "tabchange": true,
10917         /**
10918          * @event beforetabchange
10919          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
10920          * @param {Roo.TabPanel} this
10921          * @param {Object} e Set cancel to true on this object to cancel the tab change
10922          * @param {Roo.TabPanelItem} tab The tab being changed to
10923          */
10924         "beforetabchange" : true
10925     });
10926
10927     Roo.EventManager.onWindowResize(this.onResize, this);
10928     this.cpad = this.el.getPadding("lr");
10929     this.hiddenCount = 0;
10930
10931
10932     // toolbar on the tabbar support...
10933     if (this.toolbar) {
10934         var tcfg = this.toolbar;
10935         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
10936         this.toolbar = new Roo.Toolbar(tcfg);
10937         if (Roo.isSafari) {
10938             var tbl = tcfg.container.child('table', true);
10939             tbl.setAttribute('width', '100%');
10940         }
10941         
10942     }
10943    
10944
10945
10946     Roo.TabPanel.superclass.constructor.call(this);
10947 };
10948
10949 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
10950     /*
10951      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
10952      */
10953     tabPosition : "top",
10954     /*
10955      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
10956      */
10957     currentTabWidth : 0,
10958     /*
10959      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
10960      */
10961     minTabWidth : 40,
10962     /*
10963      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
10964      */
10965     maxTabWidth : 250,
10966     /*
10967      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
10968      */
10969     preferredTabWidth : 175,
10970     /*
10971      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
10972      */
10973     resizeTabs : false,
10974     /*
10975      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
10976      */
10977     monitorResize : true,
10978     /*
10979      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
10980      */
10981     toolbar : false,
10982
10983     /**
10984      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
10985      * @param {String} id The id of the div to use <b>or create</b>
10986      * @param {String} text The text for the tab
10987      * @param {String} content (optional) Content to put in the TabPanelItem body
10988      * @param {Boolean} closable (optional) True to create a close icon on the tab
10989      * @return {Roo.TabPanelItem} The created TabPanelItem
10990      */
10991     addTab : function(id, text, content, closable){
10992         var item = new Roo.TabPanelItem(this, id, text, closable);
10993         this.addTabItem(item);
10994         if(content){
10995             item.setContent(content);
10996         }
10997         return item;
10998     },
10999
11000     /**
11001      * Returns the {@link Roo.TabPanelItem} with the specified id/index
11002      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
11003      * @return {Roo.TabPanelItem}
11004      */
11005     getTab : function(id){
11006         return this.items[id];
11007     },
11008
11009     /**
11010      * Hides the {@link Roo.TabPanelItem} with the specified id/index
11011      * @param {String/Number} id The id or index of the TabPanelItem to hide.
11012      */
11013     hideTab : function(id){
11014         var t = this.items[id];
11015         if(!t.isHidden()){
11016            t.setHidden(true);
11017            this.hiddenCount++;
11018            this.autoSizeTabs();
11019         }
11020     },
11021
11022     /**
11023      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
11024      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
11025      */
11026     unhideTab : function(id){
11027         var t = this.items[id];
11028         if(t.isHidden()){
11029            t.setHidden(false);
11030            this.hiddenCount--;
11031            this.autoSizeTabs();
11032         }
11033     },
11034
11035     /**
11036      * Adds an existing {@link Roo.TabPanelItem}.
11037      * @param {Roo.TabPanelItem} item The TabPanelItem to add
11038      */
11039     addTabItem : function(item){
11040         this.items[item.id] = item;
11041         this.items.push(item);
11042         if(this.resizeTabs){
11043            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
11044            this.autoSizeTabs();
11045         }else{
11046             item.autoSize();
11047         }
11048     },
11049
11050     /**
11051      * Removes a {@link Roo.TabPanelItem}.
11052      * @param {String/Number} id The id or index of the TabPanelItem to remove.
11053      */
11054     removeTab : function(id){
11055         var items = this.items;
11056         var tab = items[id];
11057         if(!tab) { return; }
11058         var index = items.indexOf(tab);
11059         if(this.active == tab && items.length > 1){
11060             var newTab = this.getNextAvailable(index);
11061             if(newTab) {
11062                 newTab.activate();
11063             }
11064         }
11065         this.stripEl.dom.removeChild(tab.pnode.dom);
11066         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
11067             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
11068         }
11069         items.splice(index, 1);
11070         delete this.items[tab.id];
11071         tab.fireEvent("close", tab);
11072         tab.purgeListeners();
11073         this.autoSizeTabs();
11074     },
11075
11076     getNextAvailable : function(start){
11077         var items = this.items;
11078         var index = start;
11079         // look for a next tab that will slide over to
11080         // replace the one being removed
11081         while(index < items.length){
11082             var item = items[++index];
11083             if(item && !item.isHidden()){
11084                 return item;
11085             }
11086         }
11087         // if one isn't found select the previous tab (on the left)
11088         index = start;
11089         while(index >= 0){
11090             var item = items[--index];
11091             if(item && !item.isHidden()){
11092                 return item;
11093             }
11094         }
11095         return null;
11096     },
11097
11098     /**
11099      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
11100      * @param {String/Number} id The id or index of the TabPanelItem to disable.
11101      */
11102     disableTab : function(id){
11103         var tab = this.items[id];
11104         if(tab && this.active != tab){
11105             tab.disable();
11106         }
11107     },
11108
11109     /**
11110      * Enables a {@link Roo.TabPanelItem} that is disabled.
11111      * @param {String/Number} id The id or index of the TabPanelItem to enable.
11112      */
11113     enableTab : function(id){
11114         var tab = this.items[id];
11115         tab.enable();
11116     },
11117
11118     /**
11119      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
11120      * @param {String/Number} id The id or index of the TabPanelItem to activate.
11121      * @return {Roo.TabPanelItem} The TabPanelItem.
11122      */
11123     activate : function(id){
11124         var tab = this.items[id];
11125         if(!tab){
11126             return null;
11127         }
11128         if(tab == this.active || tab.disabled){
11129             return tab;
11130         }
11131         var e = {};
11132         this.fireEvent("beforetabchange", this, e, tab);
11133         if(e.cancel !== true && !tab.disabled){
11134             if(this.active){
11135                 this.active.hide();
11136             }
11137             this.active = this.items[id];
11138             this.active.show();
11139             this.fireEvent("tabchange", this, this.active);
11140         }
11141         return tab;
11142     },
11143
11144     /**
11145      * Gets the active {@link Roo.TabPanelItem}.
11146      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
11147      */
11148     getActiveTab : function(){
11149         return this.active;
11150     },
11151
11152     /**
11153      * Updates the tab body element to fit the height of the container element
11154      * for overflow scrolling
11155      * @param {Number} targetHeight (optional) Override the starting height from the elements height
11156      */
11157     syncHeight : function(targetHeight){
11158         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
11159         var bm = this.bodyEl.getMargins();
11160         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
11161         this.bodyEl.setHeight(newHeight);
11162         return newHeight;
11163     },
11164
11165     onResize : function(){
11166         if(this.monitorResize){
11167             this.autoSizeTabs();
11168         }
11169     },
11170
11171     /**
11172      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
11173      */
11174     beginUpdate : function(){
11175         this.updating = true;
11176     },
11177
11178     /**
11179      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
11180      */
11181     endUpdate : function(){
11182         this.updating = false;
11183         this.autoSizeTabs();
11184     },
11185
11186     /**
11187      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
11188      */
11189     autoSizeTabs : function(){
11190         var count = this.items.length;
11191         var vcount = count - this.hiddenCount;
11192         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
11193         var w = Math.max(this.el.getWidth() - this.cpad, 10);
11194         var availWidth = Math.floor(w / vcount);
11195         var b = this.stripBody;
11196         if(b.getWidth() > w){
11197             var tabs = this.items;
11198             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
11199             if(availWidth < this.minTabWidth){
11200                 /*if(!this.sleft){    // incomplete scrolling code
11201                     this.createScrollButtons();
11202                 }
11203                 this.showScroll();
11204                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
11205             }
11206         }else{
11207             if(this.currentTabWidth < this.preferredTabWidth){
11208                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
11209             }
11210         }
11211     },
11212
11213     /**
11214      * Returns the number of tabs in this TabPanel.
11215      * @return {Number}
11216      */
11217      getCount : function(){
11218          return this.items.length;
11219      },
11220
11221     /**
11222      * Resizes all the tabs to the passed width
11223      * @param {Number} The new width
11224      */
11225     setTabWidth : function(width){
11226         this.currentTabWidth = width;
11227         for(var i = 0, len = this.items.length; i < len; i++) {
11228                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
11229         }
11230     },
11231
11232     /**
11233      * Destroys this TabPanel
11234      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
11235      */
11236     destroy : function(removeEl){
11237         Roo.EventManager.removeResizeListener(this.onResize, this);
11238         for(var i = 0, len = this.items.length; i < len; i++){
11239             this.items[i].purgeListeners();
11240         }
11241         if(removeEl === true){
11242             this.el.update("");
11243             this.el.remove();
11244         }
11245     }
11246 });
11247
11248 /**
11249  * @class Roo.TabPanelItem
11250  * @extends Roo.util.Observable
11251  * Represents an individual item (tab plus body) in a TabPanel.
11252  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
11253  * @param {String} id The id of this TabPanelItem
11254  * @param {String} text The text for the tab of this TabPanelItem
11255  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
11256  */
11257 Roo.TabPanelItem = function(tabPanel, id, text, closable){
11258     /**
11259      * The {@link Roo.TabPanel} this TabPanelItem belongs to
11260      * @type Roo.TabPanel
11261      */
11262     this.tabPanel = tabPanel;
11263     /**
11264      * The id for this TabPanelItem
11265      * @type String
11266      */
11267     this.id = id;
11268     /** @private */
11269     this.disabled = false;
11270     /** @private */
11271     this.text = text;
11272     /** @private */
11273     this.loaded = false;
11274     this.closable = closable;
11275
11276     /**
11277      * The body element for this TabPanelItem.
11278      * @type Roo.Element
11279      */
11280     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
11281     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
11282     this.bodyEl.setStyle("display", "block");
11283     this.bodyEl.setStyle("zoom", "1");
11284     this.hideAction();
11285
11286     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
11287     /** @private */
11288     this.el = Roo.get(els.el, true);
11289     this.inner = Roo.get(els.inner, true);
11290     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
11291     this.pnode = Roo.get(els.el.parentNode, true);
11292     this.el.on("mousedown", this.onTabMouseDown, this);
11293     this.el.on("click", this.onTabClick, this);
11294     /** @private */
11295     if(closable){
11296         var c = Roo.get(els.close, true);
11297         c.dom.title = this.closeText;
11298         c.addClassOnOver("close-over");
11299         c.on("click", this.closeClick, this);
11300      }
11301
11302     this.addEvents({
11303          /**
11304          * @event activate
11305          * Fires when this tab becomes the active tab.
11306          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11307          * @param {Roo.TabPanelItem} this
11308          */
11309         "activate": true,
11310         /**
11311          * @event beforeclose
11312          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
11313          * @param {Roo.TabPanelItem} this
11314          * @param {Object} e Set cancel to true on this object to cancel the close.
11315          */
11316         "beforeclose": true,
11317         /**
11318          * @event close
11319          * Fires when this tab is closed.
11320          * @param {Roo.TabPanelItem} this
11321          */
11322          "close": true,
11323         /**
11324          * @event deactivate
11325          * Fires when this tab is no longer the active tab.
11326          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11327          * @param {Roo.TabPanelItem} this
11328          */
11329          "deactivate" : true
11330     });
11331     this.hidden = false;
11332
11333     Roo.TabPanelItem.superclass.constructor.call(this);
11334 };
11335
11336 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
11337     purgeListeners : function(){
11338        Roo.util.Observable.prototype.purgeListeners.call(this);
11339        this.el.removeAllListeners();
11340     },
11341     /**
11342      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
11343      */
11344     show : function(){
11345         this.pnode.addClass("on");
11346         this.showAction();
11347         if(Roo.isOpera){
11348             this.tabPanel.stripWrap.repaint();
11349         }
11350         this.fireEvent("activate", this.tabPanel, this);
11351     },
11352
11353     /**
11354      * Returns true if this tab is the active tab.
11355      * @return {Boolean}
11356      */
11357     isActive : function(){
11358         return this.tabPanel.getActiveTab() == this;
11359     },
11360
11361     /**
11362      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
11363      */
11364     hide : function(){
11365         this.pnode.removeClass("on");
11366         this.hideAction();
11367         this.fireEvent("deactivate", this.tabPanel, this);
11368     },
11369
11370     hideAction : function(){
11371         this.bodyEl.hide();
11372         this.bodyEl.setStyle("position", "absolute");
11373         this.bodyEl.setLeft("-20000px");
11374         this.bodyEl.setTop("-20000px");
11375     },
11376
11377     showAction : function(){
11378         this.bodyEl.setStyle("position", "relative");
11379         this.bodyEl.setTop("");
11380         this.bodyEl.setLeft("");
11381         this.bodyEl.show();
11382     },
11383
11384     /**
11385      * Set the tooltip for the tab.
11386      * @param {String} tooltip The tab's tooltip
11387      */
11388     setTooltip : function(text){
11389         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
11390             this.textEl.dom.qtip = text;
11391             this.textEl.dom.removeAttribute('title');
11392         }else{
11393             this.textEl.dom.title = text;
11394         }
11395     },
11396
11397     onTabClick : function(e){
11398         e.preventDefault();
11399         this.tabPanel.activate(this.id);
11400     },
11401
11402     onTabMouseDown : function(e){
11403         e.preventDefault();
11404         this.tabPanel.activate(this.id);
11405     },
11406
11407     getWidth : function(){
11408         return this.inner.getWidth();
11409     },
11410
11411     setWidth : function(width){
11412         var iwidth = width - this.pnode.getPadding("lr");
11413         this.inner.setWidth(iwidth);
11414         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
11415         this.pnode.setWidth(width);
11416     },
11417
11418     /**
11419      * Show or hide the tab
11420      * @param {Boolean} hidden True to hide or false to show.
11421      */
11422     setHidden : function(hidden){
11423         this.hidden = hidden;
11424         this.pnode.setStyle("display", hidden ? "none" : "");
11425     },
11426
11427     /**
11428      * Returns true if this tab is "hidden"
11429      * @return {Boolean}
11430      */
11431     isHidden : function(){
11432         return this.hidden;
11433     },
11434
11435     /**
11436      * Returns the text for this tab
11437      * @return {String}
11438      */
11439     getText : function(){
11440         return this.text;
11441     },
11442
11443     autoSize : function(){
11444         //this.el.beginMeasure();
11445         this.textEl.setWidth(1);
11446         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
11447         //this.el.endMeasure();
11448     },
11449
11450     /**
11451      * Sets the text for the tab (Note: this also sets the tooltip text)
11452      * @param {String} text The tab's text and tooltip
11453      */
11454     setText : function(text){
11455         this.text = text;
11456         this.textEl.update(text);
11457         this.setTooltip(text);
11458         if(!this.tabPanel.resizeTabs){
11459             this.autoSize();
11460         }
11461     },
11462     /**
11463      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
11464      */
11465     activate : function(){
11466         this.tabPanel.activate(this.id);
11467     },
11468
11469     /**
11470      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
11471      */
11472     disable : function(){
11473         if(this.tabPanel.active != this){
11474             this.disabled = true;
11475             this.pnode.addClass("disabled");
11476         }
11477     },
11478
11479     /**
11480      * Enables this TabPanelItem if it was previously disabled.
11481      */
11482     enable : function(){
11483         this.disabled = false;
11484         this.pnode.removeClass("disabled");
11485     },
11486
11487     /**
11488      * Sets the content for this TabPanelItem.
11489      * @param {String} content The content
11490      * @param {Boolean} loadScripts true to look for and load scripts
11491      */
11492     setContent : function(content, loadScripts){
11493         this.bodyEl.update(content, loadScripts);
11494     },
11495
11496     /**
11497      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
11498      * @return {Roo.UpdateManager} The UpdateManager
11499      */
11500     getUpdateManager : function(){
11501         return this.bodyEl.getUpdateManager();
11502     },
11503
11504     /**
11505      * Set a URL to be used to load the content for this TabPanelItem.
11506      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
11507      * @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)
11508      * @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)
11509      * @return {Roo.UpdateManager} The UpdateManager
11510      */
11511     setUrl : function(url, params, loadOnce){
11512         if(this.refreshDelegate){
11513             this.un('activate', this.refreshDelegate);
11514         }
11515         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
11516         this.on("activate", this.refreshDelegate);
11517         return this.bodyEl.getUpdateManager();
11518     },
11519
11520     /** @private */
11521     _handleRefresh : function(url, params, loadOnce){
11522         if(!loadOnce || !this.loaded){
11523             var updater = this.bodyEl.getUpdateManager();
11524             updater.update(url, params, this._setLoaded.createDelegate(this));
11525         }
11526     },
11527
11528     /**
11529      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
11530      *   Will fail silently if the setUrl method has not been called.
11531      *   This does not activate the panel, just updates its content.
11532      */
11533     refresh : function(){
11534         if(this.refreshDelegate){
11535            this.loaded = false;
11536            this.refreshDelegate();
11537         }
11538     },
11539
11540     /** @private */
11541     _setLoaded : function(){
11542         this.loaded = true;
11543     },
11544
11545     /** @private */
11546     closeClick : function(e){
11547         var o = {};
11548         e.stopEvent();
11549         this.fireEvent("beforeclose", this, o);
11550         if(o.cancel !== true){
11551             this.tabPanel.removeTab(this.id);
11552         }
11553     },
11554     /**
11555      * The text displayed in the tooltip for the close icon.
11556      * @type String
11557      */
11558     closeText : "Close this tab"
11559 });
11560
11561 /** @private */
11562 Roo.TabPanel.prototype.createStrip = function(container){
11563     var strip = document.createElement("div");
11564     strip.className = "x-tabs-wrap";
11565     container.appendChild(strip);
11566     return strip;
11567 };
11568 /** @private */
11569 Roo.TabPanel.prototype.createStripList = function(strip){
11570     // div wrapper for retard IE
11571     // returns the "tr" element.
11572     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
11573         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
11574         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
11575     return strip.firstChild.firstChild.firstChild.firstChild;
11576 };
11577 /** @private */
11578 Roo.TabPanel.prototype.createBody = function(container){
11579     var body = document.createElement("div");
11580     Roo.id(body, "tab-body");
11581     Roo.fly(body).addClass("x-tabs-body");
11582     container.appendChild(body);
11583     return body;
11584 };
11585 /** @private */
11586 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
11587     var body = Roo.getDom(id);
11588     if(!body){
11589         body = document.createElement("div");
11590         body.id = id;
11591     }
11592     Roo.fly(body).addClass("x-tabs-item-body");
11593     bodyEl.insertBefore(body, bodyEl.firstChild);
11594     return body;
11595 };
11596 /** @private */
11597 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
11598     var td = document.createElement("td");
11599     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
11600     //stripEl.appendChild(td);
11601     if(closable){
11602         td.className = "x-tabs-closable";
11603         if(!this.closeTpl){
11604             this.closeTpl = new Roo.Template(
11605                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11606                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
11607                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
11608             );
11609         }
11610         var el = this.closeTpl.overwrite(td, {"text": text});
11611         var close = el.getElementsByTagName("div")[0];
11612         var inner = el.getElementsByTagName("em")[0];
11613         return {"el": el, "close": close, "inner": inner};
11614     } else {
11615         if(!this.tabTpl){
11616             this.tabTpl = new Roo.Template(
11617                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11618                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
11619             );
11620         }
11621         var el = this.tabTpl.overwrite(td, {"text": text});
11622         var inner = el.getElementsByTagName("em")[0];
11623         return {"el": el, "inner": inner};
11624     }
11625 };/*
11626  * Based on:
11627  * Ext JS Library 1.1.1
11628  * Copyright(c) 2006-2007, Ext JS, LLC.
11629  *
11630  * Originally Released Under LGPL - original licence link has changed is not relivant.
11631  *
11632  * Fork - LGPL
11633  * <script type="text/javascript">
11634  */
11635
11636 /**
11637  * @class Roo.Button
11638  * @extends Roo.util.Observable
11639  * Simple Button class
11640  * @cfg {String} text The button text
11641  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11642  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11643  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11644  * @cfg {Object} scope The scope of the handler
11645  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11646  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11647  * @cfg {Boolean} hidden True to start hidden (defaults to false)
11648  * @cfg {Boolean} disabled True to start disabled (defaults to false)
11649  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11650  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11651    applies if enableToggle = true)
11652  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11653  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11654   an {@link Roo.util.ClickRepeater} config object (defaults to false).
11655  * @constructor
11656  * Create a new button
11657  * @param {Object} config The config object
11658  */
11659 Roo.Button = function(renderTo, config)
11660 {
11661     if (!config) {
11662         config = renderTo;
11663         renderTo = config.renderTo || false;
11664     }
11665     
11666     Roo.apply(this, config);
11667     this.addEvents({
11668         /**
11669              * @event click
11670              * Fires when this button is clicked
11671              * @param {Button} this
11672              * @param {EventObject} e The click event
11673              */
11674             "click" : true,
11675         /**
11676              * @event toggle
11677              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11678              * @param {Button} this
11679              * @param {Boolean} pressed
11680              */
11681             "toggle" : true,
11682         /**
11683              * @event mouseover
11684              * Fires when the mouse hovers over the button
11685              * @param {Button} this
11686              * @param {Event} e The event object
11687              */
11688         'mouseover' : true,
11689         /**
11690              * @event mouseout
11691              * Fires when the mouse exits the button
11692              * @param {Button} this
11693              * @param {Event} e The event object
11694              */
11695         'mouseout': true,
11696          /**
11697              * @event render
11698              * Fires when the button is rendered
11699              * @param {Button} this
11700              */
11701         'render': true
11702     });
11703     if(this.menu){
11704         this.menu = Roo.menu.MenuMgr.get(this.menu);
11705     }
11706     // register listeners first!!  - so render can be captured..
11707     Roo.util.Observable.call(this);
11708     if(renderTo){
11709         this.render(renderTo);
11710     }
11711     
11712   
11713 };
11714
11715 Roo.extend(Roo.Button, Roo.util.Observable, {
11716     /**
11717      * 
11718      */
11719     
11720     /**
11721      * Read-only. True if this button is hidden
11722      * @type Boolean
11723      */
11724     hidden : false,
11725     /**
11726      * Read-only. True if this button is disabled
11727      * @type Boolean
11728      */
11729     disabled : false,
11730     /**
11731      * Read-only. True if this button is pressed (only if enableToggle = true)
11732      * @type Boolean
11733      */
11734     pressed : false,
11735
11736     /**
11737      * @cfg {Number} tabIndex 
11738      * The DOM tabIndex for this button (defaults to undefined)
11739      */
11740     tabIndex : undefined,
11741
11742     /**
11743      * @cfg {Boolean} enableToggle
11744      * True to enable pressed/not pressed toggling (defaults to false)
11745      */
11746     enableToggle: false,
11747     /**
11748      * @cfg {Mixed} menu
11749      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11750      */
11751     menu : undefined,
11752     /**
11753      * @cfg {String} menuAlign
11754      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11755      */
11756     menuAlign : "tl-bl?",
11757
11758     /**
11759      * @cfg {String} iconCls
11760      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11761      */
11762     iconCls : undefined,
11763     /**
11764      * @cfg {String} type
11765      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
11766      */
11767     type : 'button',
11768
11769     // private
11770     menuClassTarget: 'tr',
11771
11772     /**
11773      * @cfg {String} clickEvent
11774      * The type of event to map to the button's event handler (defaults to 'click')
11775      */
11776     clickEvent : 'click',
11777
11778     /**
11779      * @cfg {Boolean} handleMouseEvents
11780      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11781      */
11782     handleMouseEvents : true,
11783
11784     /**
11785      * @cfg {String} tooltipType
11786      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11787      */
11788     tooltipType : 'qtip',
11789
11790     /**
11791      * @cfg {String} cls
11792      * A CSS class to apply to the button's main element.
11793      */
11794     
11795     /**
11796      * @cfg {Roo.Template} template (Optional)
11797      * An {@link Roo.Template} with which to create the Button's main element. This Template must
11798      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11799      * require code modifications if required elements (e.g. a button) aren't present.
11800      */
11801
11802     // private
11803     render : function(renderTo){
11804         var btn;
11805         if(this.hideParent){
11806             this.parentEl = Roo.get(renderTo);
11807         }
11808         if(!this.dhconfig){
11809             if(!this.template){
11810                 if(!Roo.Button.buttonTemplate){
11811                     // hideous table template
11812                     Roo.Button.buttonTemplate = new Roo.Template(
11813                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11814                         '<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>',
11815                         "</tr></tbody></table>");
11816                 }
11817                 this.template = Roo.Button.buttonTemplate;
11818             }
11819             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
11820             var btnEl = btn.child("button:first");
11821             btnEl.on('focus', this.onFocus, this);
11822             btnEl.on('blur', this.onBlur, this);
11823             if(this.cls){
11824                 btn.addClass(this.cls);
11825             }
11826             if(this.icon){
11827                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11828             }
11829             if(this.iconCls){
11830                 btnEl.addClass(this.iconCls);
11831                 if(!this.cls){
11832                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11833                 }
11834             }
11835             if(this.tabIndex !== undefined){
11836                 btnEl.dom.tabIndex = this.tabIndex;
11837             }
11838             if(this.tooltip){
11839                 if(typeof this.tooltip == 'object'){
11840                     Roo.QuickTips.tips(Roo.apply({
11841                           target: btnEl.id
11842                     }, this.tooltip));
11843                 } else {
11844                     btnEl.dom[this.tooltipType] = this.tooltip;
11845                 }
11846             }
11847         }else{
11848             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11849         }
11850         this.el = btn;
11851         if(this.id){
11852             this.el.dom.id = this.el.id = this.id;
11853         }
11854         if(this.menu){
11855             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11856             this.menu.on("show", this.onMenuShow, this);
11857             this.menu.on("hide", this.onMenuHide, this);
11858         }
11859         btn.addClass("x-btn");
11860         if(Roo.isIE && !Roo.isIE7){
11861             this.autoWidth.defer(1, this);
11862         }else{
11863             this.autoWidth();
11864         }
11865         if(this.handleMouseEvents){
11866             btn.on("mouseover", this.onMouseOver, this);
11867             btn.on("mouseout", this.onMouseOut, this);
11868             btn.on("mousedown", this.onMouseDown, this);
11869         }
11870         btn.on(this.clickEvent, this.onClick, this);
11871         //btn.on("mouseup", this.onMouseUp, this);
11872         if(this.hidden){
11873             this.hide();
11874         }
11875         if(this.disabled){
11876             this.disable();
11877         }
11878         Roo.ButtonToggleMgr.register(this);
11879         if(this.pressed){
11880             this.el.addClass("x-btn-pressed");
11881         }
11882         if(this.repeat){
11883             var repeater = new Roo.util.ClickRepeater(btn,
11884                 typeof this.repeat == "object" ? this.repeat : {}
11885             );
11886             repeater.on("click", this.onClick,  this);
11887         }
11888         
11889         this.fireEvent('render', this);
11890         
11891     },
11892     /**
11893      * Returns the button's underlying element
11894      * @return {Roo.Element} The element
11895      */
11896     getEl : function(){
11897         return this.el;  
11898     },
11899     
11900     /**
11901      * Destroys this Button and removes any listeners.
11902      */
11903     destroy : function(){
11904         Roo.ButtonToggleMgr.unregister(this);
11905         this.el.removeAllListeners();
11906         this.purgeListeners();
11907         this.el.remove();
11908     },
11909
11910     // private
11911     autoWidth : function(){
11912         if(this.el){
11913             this.el.setWidth("auto");
11914             if(Roo.isIE7 && Roo.isStrict){
11915                 var ib = this.el.child('button');
11916                 if(ib && ib.getWidth() > 20){
11917                     ib.clip();
11918                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11919                 }
11920             }
11921             if(this.minWidth){
11922                 if(this.hidden){
11923                     this.el.beginMeasure();
11924                 }
11925                 if(this.el.getWidth() < this.minWidth){
11926                     this.el.setWidth(this.minWidth);
11927                 }
11928                 if(this.hidden){
11929                     this.el.endMeasure();
11930                 }
11931             }
11932         }
11933     },
11934
11935     /**
11936      * Assigns this button's click handler
11937      * @param {Function} handler The function to call when the button is clicked
11938      * @param {Object} scope (optional) Scope for the function passed in
11939      */
11940     setHandler : function(handler, scope){
11941         this.handler = handler;
11942         this.scope = scope;  
11943     },
11944     
11945     /**
11946      * Sets this button's text
11947      * @param {String} text The button text
11948      */
11949     setText : function(text){
11950         this.text = text;
11951         if(this.el){
11952             this.el.child("td.x-btn-center button.x-btn-text").update(text);
11953         }
11954         this.autoWidth();
11955     },
11956     
11957     /**
11958      * Gets the text for this button
11959      * @return {String} The button text
11960      */
11961     getText : function(){
11962         return this.text;  
11963     },
11964     
11965     /**
11966      * Show this button
11967      */
11968     show: function(){
11969         this.hidden = false;
11970         if(this.el){
11971             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
11972         }
11973     },
11974     
11975     /**
11976      * Hide this button
11977      */
11978     hide: function(){
11979         this.hidden = true;
11980         if(this.el){
11981             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
11982         }
11983     },
11984     
11985     /**
11986      * Convenience function for boolean show/hide
11987      * @param {Boolean} visible True to show, false to hide
11988      */
11989     setVisible: function(visible){
11990         if(visible) {
11991             this.show();
11992         }else{
11993             this.hide();
11994         }
11995     },
11996     
11997     /**
11998      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
11999      * @param {Boolean} state (optional) Force a particular state
12000      */
12001     toggle : function(state){
12002         state = state === undefined ? !this.pressed : state;
12003         if(state != this.pressed){
12004             if(state){
12005                 this.el.addClass("x-btn-pressed");
12006                 this.pressed = true;
12007                 this.fireEvent("toggle", this, true);
12008             }else{
12009                 this.el.removeClass("x-btn-pressed");
12010                 this.pressed = false;
12011                 this.fireEvent("toggle", this, false);
12012             }
12013             if(this.toggleHandler){
12014                 this.toggleHandler.call(this.scope || this, this, state);
12015             }
12016         }
12017     },
12018     
12019     /**
12020      * Focus the button
12021      */
12022     focus : function(){
12023         this.el.child('button:first').focus();
12024     },
12025     
12026     /**
12027      * Disable this button
12028      */
12029     disable : function(){
12030         if(this.el){
12031             this.el.addClass("x-btn-disabled");
12032         }
12033         this.disabled = true;
12034     },
12035     
12036     /**
12037      * Enable this button
12038      */
12039     enable : function(){
12040         if(this.el){
12041             this.el.removeClass("x-btn-disabled");
12042         }
12043         this.disabled = false;
12044     },
12045
12046     /**
12047      * Convenience function for boolean enable/disable
12048      * @param {Boolean} enabled True to enable, false to disable
12049      */
12050     setDisabled : function(v){
12051         this[v !== true ? "enable" : "disable"]();
12052     },
12053
12054     // private
12055     onClick : function(e){
12056         if(e){
12057             e.preventDefault();
12058         }
12059         if(e.button != 0){
12060             return;
12061         }
12062         if(!this.disabled){
12063             if(this.enableToggle){
12064                 this.toggle();
12065             }
12066             if(this.menu && !this.menu.isVisible()){
12067                 this.menu.show(this.el, this.menuAlign);
12068             }
12069             this.fireEvent("click", this, e);
12070             if(this.handler){
12071                 this.el.removeClass("x-btn-over");
12072                 this.handler.call(this.scope || this, this, e);
12073             }
12074         }
12075     },
12076     // private
12077     onMouseOver : function(e){
12078         if(!this.disabled){
12079             this.el.addClass("x-btn-over");
12080             this.fireEvent('mouseover', this, e);
12081         }
12082     },
12083     // private
12084     onMouseOut : function(e){
12085         if(!e.within(this.el,  true)){
12086             this.el.removeClass("x-btn-over");
12087             this.fireEvent('mouseout', this, e);
12088         }
12089     },
12090     // private
12091     onFocus : function(e){
12092         if(!this.disabled){
12093             this.el.addClass("x-btn-focus");
12094         }
12095     },
12096     // private
12097     onBlur : function(e){
12098         this.el.removeClass("x-btn-focus");
12099     },
12100     // private
12101     onMouseDown : function(e){
12102         if(!this.disabled && e.button == 0){
12103             this.el.addClass("x-btn-click");
12104             Roo.get(document).on('mouseup', this.onMouseUp, this);
12105         }
12106     },
12107     // private
12108     onMouseUp : function(e){
12109         if(e.button == 0){
12110             this.el.removeClass("x-btn-click");
12111             Roo.get(document).un('mouseup', this.onMouseUp, this);
12112         }
12113     },
12114     // private
12115     onMenuShow : function(e){
12116         this.el.addClass("x-btn-menu-active");
12117     },
12118     // private
12119     onMenuHide : function(e){
12120         this.el.removeClass("x-btn-menu-active");
12121     }   
12122 });
12123
12124 // Private utility class used by Button
12125 Roo.ButtonToggleMgr = function(){
12126    var groups = {};
12127    
12128    function toggleGroup(btn, state){
12129        if(state){
12130            var g = groups[btn.toggleGroup];
12131            for(var i = 0, l = g.length; i < l; i++){
12132                if(g[i] != btn){
12133                    g[i].toggle(false);
12134                }
12135            }
12136        }
12137    }
12138    
12139    return {
12140        register : function(btn){
12141            if(!btn.toggleGroup){
12142                return;
12143            }
12144            var g = groups[btn.toggleGroup];
12145            if(!g){
12146                g = groups[btn.toggleGroup] = [];
12147            }
12148            g.push(btn);
12149            btn.on("toggle", toggleGroup);
12150        },
12151        
12152        unregister : function(btn){
12153            if(!btn.toggleGroup){
12154                return;
12155            }
12156            var g = groups[btn.toggleGroup];
12157            if(g){
12158                g.remove(btn);
12159                btn.un("toggle", toggleGroup);
12160            }
12161        }
12162    };
12163 }();/*
12164  * Based on:
12165  * Ext JS Library 1.1.1
12166  * Copyright(c) 2006-2007, Ext JS, LLC.
12167  *
12168  * Originally Released Under LGPL - original licence link has changed is not relivant.
12169  *
12170  * Fork - LGPL
12171  * <script type="text/javascript">
12172  */
12173  
12174 /**
12175  * @class Roo.SplitButton
12176  * @extends Roo.Button
12177  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
12178  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
12179  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
12180  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
12181  * @cfg {String} arrowTooltip The title attribute of the arrow
12182  * @constructor
12183  * Create a new menu button
12184  * @param {String/HTMLElement/Element} renderTo The element to append the button to
12185  * @param {Object} config The config object
12186  */
12187 Roo.SplitButton = function(renderTo, config){
12188     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
12189     /**
12190      * @event arrowclick
12191      * Fires when this button's arrow is clicked
12192      * @param {SplitButton} this
12193      * @param {EventObject} e The click event
12194      */
12195     this.addEvents({"arrowclick":true});
12196 };
12197
12198 Roo.extend(Roo.SplitButton, Roo.Button, {
12199     render : function(renderTo){
12200         // this is one sweet looking template!
12201         var tpl = new Roo.Template(
12202             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
12203             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
12204             '<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>',
12205             "</tbody></table></td><td>",
12206             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
12207             '<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>',
12208             "</tbody></table></td></tr></table>"
12209         );
12210         var btn = tpl.append(renderTo, [this.text, this.type], true);
12211         var btnEl = btn.child("button");
12212         if(this.cls){
12213             btn.addClass(this.cls);
12214         }
12215         if(this.icon){
12216             btnEl.setStyle('background-image', 'url(' +this.icon +')');
12217         }
12218         if(this.iconCls){
12219             btnEl.addClass(this.iconCls);
12220             if(!this.cls){
12221                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
12222             }
12223         }
12224         this.el = btn;
12225         if(this.handleMouseEvents){
12226             btn.on("mouseover", this.onMouseOver, this);
12227             btn.on("mouseout", this.onMouseOut, this);
12228             btn.on("mousedown", this.onMouseDown, this);
12229             btn.on("mouseup", this.onMouseUp, this);
12230         }
12231         btn.on(this.clickEvent, this.onClick, this);
12232         if(this.tooltip){
12233             if(typeof this.tooltip == 'object'){
12234                 Roo.QuickTips.tips(Roo.apply({
12235                       target: btnEl.id
12236                 }, this.tooltip));
12237             } else {
12238                 btnEl.dom[this.tooltipType] = this.tooltip;
12239             }
12240         }
12241         if(this.arrowTooltip){
12242             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
12243         }
12244         if(this.hidden){
12245             this.hide();
12246         }
12247         if(this.disabled){
12248             this.disable();
12249         }
12250         if(this.pressed){
12251             this.el.addClass("x-btn-pressed");
12252         }
12253         if(Roo.isIE && !Roo.isIE7){
12254             this.autoWidth.defer(1, this);
12255         }else{
12256             this.autoWidth();
12257         }
12258         if(this.menu){
12259             this.menu.on("show", this.onMenuShow, this);
12260             this.menu.on("hide", this.onMenuHide, this);
12261         }
12262         this.fireEvent('render', this);
12263     },
12264
12265     // private
12266     autoWidth : function(){
12267         if(this.el){
12268             var tbl = this.el.child("table:first");
12269             var tbl2 = this.el.child("table:last");
12270             this.el.setWidth("auto");
12271             tbl.setWidth("auto");
12272             if(Roo.isIE7 && Roo.isStrict){
12273                 var ib = this.el.child('button:first');
12274                 if(ib && ib.getWidth() > 20){
12275                     ib.clip();
12276                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
12277                 }
12278             }
12279             if(this.minWidth){
12280                 if(this.hidden){
12281                     this.el.beginMeasure();
12282                 }
12283                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
12284                     tbl.setWidth(this.minWidth-tbl2.getWidth());
12285                 }
12286                 if(this.hidden){
12287                     this.el.endMeasure();
12288                 }
12289             }
12290             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
12291         } 
12292     },
12293     /**
12294      * Sets this button's click handler
12295      * @param {Function} handler The function to call when the button is clicked
12296      * @param {Object} scope (optional) Scope for the function passed above
12297      */
12298     setHandler : function(handler, scope){
12299         this.handler = handler;
12300         this.scope = scope;  
12301     },
12302     
12303     /**
12304      * Sets this button's arrow click handler
12305      * @param {Function} handler The function to call when the arrow is clicked
12306      * @param {Object} scope (optional) Scope for the function passed above
12307      */
12308     setArrowHandler : function(handler, scope){
12309         this.arrowHandler = handler;
12310         this.scope = scope;  
12311     },
12312     
12313     /**
12314      * Focus the button
12315      */
12316     focus : function(){
12317         if(this.el){
12318             this.el.child("button:first").focus();
12319         }
12320     },
12321
12322     // private
12323     onClick : function(e){
12324         e.preventDefault();
12325         if(!this.disabled){
12326             if(e.getTarget(".x-btn-menu-arrow-wrap")){
12327                 if(this.menu && !this.menu.isVisible()){
12328                     this.menu.show(this.el, this.menuAlign);
12329                 }
12330                 this.fireEvent("arrowclick", this, e);
12331                 if(this.arrowHandler){
12332                     this.arrowHandler.call(this.scope || this, this, e);
12333                 }
12334             }else{
12335                 this.fireEvent("click", this, e);
12336                 if(this.handler){
12337                     this.handler.call(this.scope || this, this, e);
12338                 }
12339             }
12340         }
12341     },
12342     // private
12343     onMouseDown : function(e){
12344         if(!this.disabled){
12345             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
12346         }
12347     },
12348     // private
12349     onMouseUp : function(e){
12350         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
12351     }   
12352 });
12353
12354
12355 // backwards compat
12356 Roo.MenuButton = Roo.SplitButton;/*
12357  * Based on:
12358  * Ext JS Library 1.1.1
12359  * Copyright(c) 2006-2007, Ext JS, LLC.
12360  *
12361  * Originally Released Under LGPL - original licence link has changed is not relivant.
12362  *
12363  * Fork - LGPL
12364  * <script type="text/javascript">
12365  */
12366
12367 /**
12368  * @class Roo.Toolbar
12369  * Basic Toolbar class.
12370  * @constructor
12371  * Creates a new Toolbar
12372  * @param {Object} container The config object
12373  */ 
12374 Roo.Toolbar = function(container, buttons, config)
12375 {
12376     /// old consturctor format still supported..
12377     if(container instanceof Array){ // omit the container for later rendering
12378         buttons = container;
12379         config = buttons;
12380         container = null;
12381     }
12382     if (typeof(container) == 'object' && container.xtype) {
12383         config = container;
12384         container = config.container;
12385         buttons = config.buttons || []; // not really - use items!!
12386     }
12387     var xitems = [];
12388     if (config && config.items) {
12389         xitems = config.items;
12390         delete config.items;
12391     }
12392     Roo.apply(this, config);
12393     this.buttons = buttons;
12394     
12395     if(container){
12396         this.render(container);
12397     }
12398     this.xitems = xitems;
12399     Roo.each(xitems, function(b) {
12400         this.add(b);
12401     }, this);
12402     
12403 };
12404
12405 Roo.Toolbar.prototype = {
12406     /**
12407      * @cfg {Array} items
12408      * array of button configs or elements to add (will be converted to a MixedCollection)
12409      */
12410     
12411     /**
12412      * @cfg {String/HTMLElement/Element} container
12413      * The id or element that will contain the toolbar
12414      */
12415     // private
12416     render : function(ct){
12417         this.el = Roo.get(ct);
12418         if(this.cls){
12419             this.el.addClass(this.cls);
12420         }
12421         // using a table allows for vertical alignment
12422         // 100% width is needed by Safari...
12423         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
12424         this.tr = this.el.child("tr", true);
12425         var autoId = 0;
12426         this.items = new Roo.util.MixedCollection(false, function(o){
12427             return o.id || ("item" + (++autoId));
12428         });
12429         if(this.buttons){
12430             this.add.apply(this, this.buttons);
12431             delete this.buttons;
12432         }
12433     },
12434
12435     /**
12436      * Adds element(s) to the toolbar -- this function takes a variable number of 
12437      * arguments of mixed type and adds them to the toolbar.
12438      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
12439      * <ul>
12440      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
12441      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
12442      * <li>Field: Any form field (equivalent to {@link #addField})</li>
12443      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
12444      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
12445      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
12446      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
12447      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
12448      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
12449      * </ul>
12450      * @param {Mixed} arg2
12451      * @param {Mixed} etc.
12452      */
12453     add : function(){
12454         var a = arguments, l = a.length;
12455         for(var i = 0; i < l; i++){
12456             this._add(a[i]);
12457         }
12458     },
12459     // private..
12460     _add : function(el) {
12461         
12462         if (el.xtype) {
12463             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
12464         }
12465         
12466         if (el.applyTo){ // some kind of form field
12467             return this.addField(el);
12468         } 
12469         if (el.render){ // some kind of Toolbar.Item
12470             return this.addItem(el);
12471         }
12472         if (typeof el == "string"){ // string
12473             if(el == "separator" || el == "-"){
12474                 return this.addSeparator();
12475             }
12476             if (el == " "){
12477                 return this.addSpacer();
12478             }
12479             if(el == "->"){
12480                 return this.addFill();
12481             }
12482             return this.addText(el);
12483             
12484         }
12485         if(el.tagName){ // element
12486             return this.addElement(el);
12487         }
12488         if(typeof el == "object"){ // must be button config?
12489             return this.addButton(el);
12490         }
12491         // and now what?!?!
12492         return false;
12493         
12494     },
12495     
12496     /**
12497      * Add an Xtype element
12498      * @param {Object} xtype Xtype Object
12499      * @return {Object} created Object
12500      */
12501     addxtype : function(e){
12502         return this.add(e);  
12503     },
12504     
12505     /**
12506      * Returns the Element for this toolbar.
12507      * @return {Roo.Element}
12508      */
12509     getEl : function(){
12510         return this.el;  
12511     },
12512     
12513     /**
12514      * Adds a separator
12515      * @return {Roo.Toolbar.Item} The separator item
12516      */
12517     addSeparator : function(){
12518         return this.addItem(new Roo.Toolbar.Separator());
12519     },
12520
12521     /**
12522      * Adds a spacer element
12523      * @return {Roo.Toolbar.Spacer} The spacer item
12524      */
12525     addSpacer : function(){
12526         return this.addItem(new Roo.Toolbar.Spacer());
12527     },
12528
12529     /**
12530      * Adds a fill element that forces subsequent additions to the right side of the toolbar
12531      * @return {Roo.Toolbar.Fill} The fill item
12532      */
12533     addFill : function(){
12534         return this.addItem(new Roo.Toolbar.Fill());
12535     },
12536
12537     /**
12538      * Adds any standard HTML element to the toolbar
12539      * @param {String/HTMLElement/Element} el The element or id of the element to add
12540      * @return {Roo.Toolbar.Item} The element's item
12541      */
12542     addElement : function(el){
12543         return this.addItem(new Roo.Toolbar.Item(el));
12544     },
12545     /**
12546      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
12547      * @type Roo.util.MixedCollection  
12548      */
12549     items : false,
12550      
12551     /**
12552      * Adds any Toolbar.Item or subclass
12553      * @param {Roo.Toolbar.Item} item
12554      * @return {Roo.Toolbar.Item} The item
12555      */
12556     addItem : function(item){
12557         var td = this.nextBlock();
12558         item.render(td);
12559         this.items.add(item);
12560         return item;
12561     },
12562     
12563     /**
12564      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
12565      * @param {Object/Array} config A button config or array of configs
12566      * @return {Roo.Toolbar.Button/Array}
12567      */
12568     addButton : function(config){
12569         if(config instanceof Array){
12570             var buttons = [];
12571             for(var i = 0, len = config.length; i < len; i++) {
12572                 buttons.push(this.addButton(config[i]));
12573             }
12574             return buttons;
12575         }
12576         var b = config;
12577         if(!(config instanceof Roo.Toolbar.Button)){
12578             b = config.split ?
12579                 new Roo.Toolbar.SplitButton(config) :
12580                 new Roo.Toolbar.Button(config);
12581         }
12582         var td = this.nextBlock();
12583         b.render(td);
12584         this.items.add(b);
12585         return b;
12586     },
12587     
12588     /**
12589      * Adds text to the toolbar
12590      * @param {String} text The text to add
12591      * @return {Roo.Toolbar.Item} The element's item
12592      */
12593     addText : function(text){
12594         return this.addItem(new Roo.Toolbar.TextItem(text));
12595     },
12596     
12597     /**
12598      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
12599      * @param {Number} index The index where the item is to be inserted
12600      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
12601      * @return {Roo.Toolbar.Button/Item}
12602      */
12603     insertButton : function(index, item){
12604         if(item instanceof Array){
12605             var buttons = [];
12606             for(var i = 0, len = item.length; i < len; i++) {
12607                buttons.push(this.insertButton(index + i, item[i]));
12608             }
12609             return buttons;
12610         }
12611         if (!(item instanceof Roo.Toolbar.Button)){
12612            item = new Roo.Toolbar.Button(item);
12613         }
12614         var td = document.createElement("td");
12615         this.tr.insertBefore(td, this.tr.childNodes[index]);
12616         item.render(td);
12617         this.items.insert(index, item);
12618         return item;
12619     },
12620     
12621     /**
12622      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12623      * @param {Object} config
12624      * @return {Roo.Toolbar.Item} The element's item
12625      */
12626     addDom : function(config, returnEl){
12627         var td = this.nextBlock();
12628         Roo.DomHelper.overwrite(td, config);
12629         var ti = new Roo.Toolbar.Item(td.firstChild);
12630         ti.render(td);
12631         this.items.add(ti);
12632         return ti;
12633     },
12634
12635     /**
12636      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12637      * @type Roo.util.MixedCollection  
12638      */
12639     fields : false,
12640     
12641     /**
12642      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
12643      * Note: the field should not have been rendered yet. For a field that has already been
12644      * rendered, use {@link #addElement}.
12645      * @param {Roo.form.Field} field
12646      * @return {Roo.ToolbarItem}
12647      */
12648      
12649       
12650     addField : function(field) {
12651         if (!this.fields) {
12652             var autoId = 0;
12653             this.fields = new Roo.util.MixedCollection(false, function(o){
12654                 return o.id || ("item" + (++autoId));
12655             });
12656
12657         }
12658         
12659         var td = this.nextBlock();
12660         field.render(td);
12661         var ti = new Roo.Toolbar.Item(td.firstChild);
12662         ti.render(td);
12663         this.items.add(ti);
12664         this.fields.add(field);
12665         return ti;
12666     },
12667     /**
12668      * Hide the toolbar
12669      * @method hide
12670      */
12671      
12672       
12673     hide : function()
12674     {
12675         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12676         this.el.child('div').hide();
12677     },
12678     /**
12679      * Show the toolbar
12680      * @method show
12681      */
12682     show : function()
12683     {
12684         this.el.child('div').show();
12685     },
12686       
12687     // private
12688     nextBlock : function(){
12689         var td = document.createElement("td");
12690         this.tr.appendChild(td);
12691         return td;
12692     },
12693
12694     // private
12695     destroy : function(){
12696         if(this.items){ // rendered?
12697             Roo.destroy.apply(Roo, this.items.items);
12698         }
12699         if(this.fields){ // rendered?
12700             Roo.destroy.apply(Roo, this.fields.items);
12701         }
12702         Roo.Element.uncache(this.el, this.tr);
12703     }
12704 };
12705
12706 /**
12707  * @class Roo.Toolbar.Item
12708  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12709  * @constructor
12710  * Creates a new Item
12711  * @param {HTMLElement} el 
12712  */
12713 Roo.Toolbar.Item = function(el){
12714     this.el = Roo.getDom(el);
12715     this.id = Roo.id(this.el);
12716     this.hidden = false;
12717 };
12718
12719 Roo.Toolbar.Item.prototype = {
12720     
12721     /**
12722      * Get this item's HTML Element
12723      * @return {HTMLElement}
12724      */
12725     getEl : function(){
12726        return this.el;  
12727     },
12728
12729     // private
12730     render : function(td){
12731         this.td = td;
12732         td.appendChild(this.el);
12733     },
12734     
12735     /**
12736      * Removes and destroys this item.
12737      */
12738     destroy : function(){
12739         this.td.parentNode.removeChild(this.td);
12740     },
12741     
12742     /**
12743      * Shows this item.
12744      */
12745     show: function(){
12746         this.hidden = false;
12747         this.td.style.display = "";
12748     },
12749     
12750     /**
12751      * Hides this item.
12752      */
12753     hide: function(){
12754         this.hidden = true;
12755         this.td.style.display = "none";
12756     },
12757     
12758     /**
12759      * Convenience function for boolean show/hide.
12760      * @param {Boolean} visible true to show/false to hide
12761      */
12762     setVisible: function(visible){
12763         if(visible) {
12764             this.show();
12765         }else{
12766             this.hide();
12767         }
12768     },
12769     
12770     /**
12771      * Try to focus this item.
12772      */
12773     focus : function(){
12774         Roo.fly(this.el).focus();
12775     },
12776     
12777     /**
12778      * Disables this item.
12779      */
12780     disable : function(){
12781         Roo.fly(this.td).addClass("x-item-disabled");
12782         this.disabled = true;
12783         this.el.disabled = true;
12784     },
12785     
12786     /**
12787      * Enables this item.
12788      */
12789     enable : function(){
12790         Roo.fly(this.td).removeClass("x-item-disabled");
12791         this.disabled = false;
12792         this.el.disabled = false;
12793     }
12794 };
12795
12796
12797 /**
12798  * @class Roo.Toolbar.Separator
12799  * @extends Roo.Toolbar.Item
12800  * A simple toolbar separator class
12801  * @constructor
12802  * Creates a new Separator
12803  */
12804 Roo.Toolbar.Separator = function(){
12805     var s = document.createElement("span");
12806     s.className = "ytb-sep";
12807     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
12808 };
12809 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12810     enable:Roo.emptyFn,
12811     disable:Roo.emptyFn,
12812     focus:Roo.emptyFn
12813 });
12814
12815 /**
12816  * @class Roo.Toolbar.Spacer
12817  * @extends Roo.Toolbar.Item
12818  * A simple element that adds extra horizontal space to a toolbar.
12819  * @constructor
12820  * Creates a new Spacer
12821  */
12822 Roo.Toolbar.Spacer = function(){
12823     var s = document.createElement("div");
12824     s.className = "ytb-spacer";
12825     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
12826 };
12827 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12828     enable:Roo.emptyFn,
12829     disable:Roo.emptyFn,
12830     focus:Roo.emptyFn
12831 });
12832
12833 /**
12834  * @class Roo.Toolbar.Fill
12835  * @extends Roo.Toolbar.Spacer
12836  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12837  * @constructor
12838  * Creates a new Spacer
12839  */
12840 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12841     // private
12842     render : function(td){
12843         td.style.width = '100%';
12844         Roo.Toolbar.Fill.superclass.render.call(this, td);
12845     }
12846 });
12847
12848 /**
12849  * @class Roo.Toolbar.TextItem
12850  * @extends Roo.Toolbar.Item
12851  * A simple class that renders text directly into a toolbar.
12852  * @constructor
12853  * Creates a new TextItem
12854  * @param {String} text
12855  */
12856 Roo.Toolbar.TextItem = function(text){
12857     if (typeof(text) == 'object') {
12858         text = text.text;
12859     }
12860     var s = document.createElement("span");
12861     s.className = "ytb-text";
12862     s.innerHTML = text;
12863     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
12864 };
12865 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12866     enable:Roo.emptyFn,
12867     disable:Roo.emptyFn,
12868     focus:Roo.emptyFn
12869 });
12870
12871 /**
12872  * @class Roo.Toolbar.Button
12873  * @extends Roo.Button
12874  * A button that renders into a toolbar.
12875  * @constructor
12876  * Creates a new Button
12877  * @param {Object} config A standard {@link Roo.Button} config object
12878  */
12879 Roo.Toolbar.Button = function(config){
12880     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12881 };
12882 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12883     render : function(td){
12884         this.td = td;
12885         Roo.Toolbar.Button.superclass.render.call(this, td);
12886     },
12887     
12888     /**
12889      * Removes and destroys this button
12890      */
12891     destroy : function(){
12892         Roo.Toolbar.Button.superclass.destroy.call(this);
12893         this.td.parentNode.removeChild(this.td);
12894     },
12895     
12896     /**
12897      * Shows this button
12898      */
12899     show: function(){
12900         this.hidden = false;
12901         this.td.style.display = "";
12902     },
12903     
12904     /**
12905      * Hides this button
12906      */
12907     hide: function(){
12908         this.hidden = true;
12909         this.td.style.display = "none";
12910     },
12911
12912     /**
12913      * Disables this item
12914      */
12915     disable : function(){
12916         Roo.fly(this.td).addClass("x-item-disabled");
12917         this.disabled = true;
12918     },
12919
12920     /**
12921      * Enables this item
12922      */
12923     enable : function(){
12924         Roo.fly(this.td).removeClass("x-item-disabled");
12925         this.disabled = false;
12926     }
12927 });
12928 // backwards compat
12929 Roo.ToolbarButton = Roo.Toolbar.Button;
12930
12931 /**
12932  * @class Roo.Toolbar.SplitButton
12933  * @extends Roo.SplitButton
12934  * A menu button that renders into a toolbar.
12935  * @constructor
12936  * Creates a new SplitButton
12937  * @param {Object} config A standard {@link Roo.SplitButton} config object
12938  */
12939 Roo.Toolbar.SplitButton = function(config){
12940     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
12941 };
12942 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
12943     render : function(td){
12944         this.td = td;
12945         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
12946     },
12947     
12948     /**
12949      * Removes and destroys this button
12950      */
12951     destroy : function(){
12952         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
12953         this.td.parentNode.removeChild(this.td);
12954     },
12955     
12956     /**
12957      * Shows this button
12958      */
12959     show: function(){
12960         this.hidden = false;
12961         this.td.style.display = "";
12962     },
12963     
12964     /**
12965      * Hides this button
12966      */
12967     hide: function(){
12968         this.hidden = true;
12969         this.td.style.display = "none";
12970     }
12971 });
12972
12973 // backwards compat
12974 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
12975  * Based on:
12976  * Ext JS Library 1.1.1
12977  * Copyright(c) 2006-2007, Ext JS, LLC.
12978  *
12979  * Originally Released Under LGPL - original licence link has changed is not relivant.
12980  *
12981  * Fork - LGPL
12982  * <script type="text/javascript">
12983  */
12984  
12985 /**
12986  * @class Roo.PagingToolbar
12987  * @extends Roo.Toolbar
12988  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
12989  * @constructor
12990  * Create a new PagingToolbar
12991  * @param {Object} config The config object
12992  */
12993 Roo.PagingToolbar = function(el, ds, config)
12994 {
12995     // old args format still supported... - xtype is prefered..
12996     if (typeof(el) == 'object' && el.xtype) {
12997         // created from xtype...
12998         config = el;
12999         ds = el.dataSource;
13000         el = config.container;
13001     }
13002     var items = [];
13003     if (config.items) {
13004         items = config.items;
13005         config.items = [];
13006     }
13007     
13008     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
13009     this.ds = ds;
13010     this.cursor = 0;
13011     this.renderButtons(this.el);
13012     this.bind(ds);
13013     
13014     // supprot items array.
13015    
13016     Roo.each(items, function(e) {
13017         this.add(Roo.factory(e));
13018     },this);
13019     
13020 };
13021
13022 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
13023     /**
13024      * @cfg {Roo.data.Store} dataSource
13025      * The underlying data store providing the paged data
13026      */
13027     /**
13028      * @cfg {String/HTMLElement/Element} container
13029      * container The id or element that will contain the toolbar
13030      */
13031     /**
13032      * @cfg {Boolean} displayInfo
13033      * True to display the displayMsg (defaults to false)
13034      */
13035     /**
13036      * @cfg {Number} pageSize
13037      * The number of records to display per page (defaults to 20)
13038      */
13039     pageSize: 20,
13040     /**
13041      * @cfg {String} displayMsg
13042      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
13043      */
13044     displayMsg : 'Displaying {0} - {1} of {2}',
13045     /**
13046      * @cfg {String} emptyMsg
13047      * The message to display when no records are found (defaults to "No data to display")
13048      */
13049     emptyMsg : 'No data to display',
13050     /**
13051      * Customizable piece of the default paging text (defaults to "Page")
13052      * @type String
13053      */
13054     beforePageText : "Page",
13055     /**
13056      * Customizable piece of the default paging text (defaults to "of %0")
13057      * @type String
13058      */
13059     afterPageText : "of {0}",
13060     /**
13061      * Customizable piece of the default paging text (defaults to "First Page")
13062      * @type String
13063      */
13064     firstText : "First Page",
13065     /**
13066      * Customizable piece of the default paging text (defaults to "Previous Page")
13067      * @type String
13068      */
13069     prevText : "Previous Page",
13070     /**
13071      * Customizable piece of the default paging text (defaults to "Next Page")
13072      * @type String
13073      */
13074     nextText : "Next Page",
13075     /**
13076      * Customizable piece of the default paging text (defaults to "Last Page")
13077      * @type String
13078      */
13079     lastText : "Last Page",
13080     /**
13081      * Customizable piece of the default paging text (defaults to "Refresh")
13082      * @type String
13083      */
13084     refreshText : "Refresh",
13085
13086     // private
13087     renderButtons : function(el){
13088         Roo.PagingToolbar.superclass.render.call(this, el);
13089         this.first = this.addButton({
13090             tooltip: this.firstText,
13091             cls: "x-btn-icon x-grid-page-first",
13092             disabled: true,
13093             handler: this.onClick.createDelegate(this, ["first"])
13094         });
13095         this.prev = this.addButton({
13096             tooltip: this.prevText,
13097             cls: "x-btn-icon x-grid-page-prev",
13098             disabled: true,
13099             handler: this.onClick.createDelegate(this, ["prev"])
13100         });
13101         //this.addSeparator();
13102         this.add(this.beforePageText);
13103         this.field = Roo.get(this.addDom({
13104            tag: "input",
13105            type: "text",
13106            size: "3",
13107            value: "1",
13108            cls: "x-grid-page-number"
13109         }).el);
13110         this.field.on("keydown", this.onPagingKeydown, this);
13111         this.field.on("focus", function(){this.dom.select();});
13112         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
13113         this.field.setHeight(18);
13114         //this.addSeparator();
13115         this.next = this.addButton({
13116             tooltip: this.nextText,
13117             cls: "x-btn-icon x-grid-page-next",
13118             disabled: true,
13119             handler: this.onClick.createDelegate(this, ["next"])
13120         });
13121         this.last = this.addButton({
13122             tooltip: this.lastText,
13123             cls: "x-btn-icon x-grid-page-last",
13124             disabled: true,
13125             handler: this.onClick.createDelegate(this, ["last"])
13126         });
13127         //this.addSeparator();
13128         this.loading = this.addButton({
13129             tooltip: this.refreshText,
13130             cls: "x-btn-icon x-grid-loading",
13131             handler: this.onClick.createDelegate(this, ["refresh"])
13132         });
13133
13134         if(this.displayInfo){
13135             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
13136         }
13137     },
13138
13139     // private
13140     updateInfo : function(){
13141         if(this.displayEl){
13142             var count = this.ds.getCount();
13143             var msg = count == 0 ?
13144                 this.emptyMsg :
13145                 String.format(
13146                     this.displayMsg,
13147                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
13148                 );
13149             this.displayEl.update(msg);
13150         }
13151     },
13152
13153     // private
13154     onLoad : function(ds, r, o){
13155        this.cursor = o.params ? o.params.start : 0;
13156        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
13157
13158        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
13159        this.field.dom.value = ap;
13160        this.first.setDisabled(ap == 1);
13161        this.prev.setDisabled(ap == 1);
13162        this.next.setDisabled(ap == ps);
13163        this.last.setDisabled(ap == ps);
13164        this.loading.enable();
13165        this.updateInfo();
13166     },
13167
13168     // private
13169     getPageData : function(){
13170         var total = this.ds.getTotalCount();
13171         return {
13172             total : total,
13173             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
13174             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
13175         };
13176     },
13177
13178     // private
13179     onLoadError : function(){
13180         this.loading.enable();
13181     },
13182
13183     // private
13184     onPagingKeydown : function(e){
13185         var k = e.getKey();
13186         var d = this.getPageData();
13187         if(k == e.RETURN){
13188             var v = this.field.dom.value, pageNum;
13189             if(!v || isNaN(pageNum = parseInt(v, 10))){
13190                 this.field.dom.value = d.activePage;
13191                 return;
13192             }
13193             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
13194             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13195             e.stopEvent();
13196         }
13197         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))
13198         {
13199           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
13200           this.field.dom.value = pageNum;
13201           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
13202           e.stopEvent();
13203         }
13204         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13205         {
13206           var v = this.field.dom.value, pageNum; 
13207           var increment = (e.shiftKey) ? 10 : 1;
13208           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13209             increment *= -1;
13210           if(!v || isNaN(pageNum = parseInt(v, 10))) {
13211             this.field.dom.value = d.activePage;
13212             return;
13213           }
13214           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
13215           {
13216             this.field.dom.value = parseInt(v, 10) + increment;
13217             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
13218             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13219           }
13220           e.stopEvent();
13221         }
13222     },
13223
13224     // private
13225     beforeLoad : function(){
13226         if(this.loading){
13227             this.loading.disable();
13228         }
13229     },
13230
13231     // private
13232     onClick : function(which){
13233         var ds = this.ds;
13234         switch(which){
13235             case "first":
13236                 ds.load({params:{start: 0, limit: this.pageSize}});
13237             break;
13238             case "prev":
13239                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
13240             break;
13241             case "next":
13242                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
13243             break;
13244             case "last":
13245                 var total = ds.getTotalCount();
13246                 var extra = total % this.pageSize;
13247                 var lastStart = extra ? (total - extra) : total-this.pageSize;
13248                 ds.load({params:{start: lastStart, limit: this.pageSize}});
13249             break;
13250             case "refresh":
13251                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
13252             break;
13253         }
13254     },
13255
13256     /**
13257      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
13258      * @param {Roo.data.Store} store The data store to unbind
13259      */
13260     unbind : function(ds){
13261         ds.un("beforeload", this.beforeLoad, this);
13262         ds.un("load", this.onLoad, this);
13263         ds.un("loadexception", this.onLoadError, this);
13264         ds.un("remove", this.updateInfo, this);
13265         ds.un("add", this.updateInfo, this);
13266         this.ds = undefined;
13267     },
13268
13269     /**
13270      * Binds the paging toolbar to the specified {@link Roo.data.Store}
13271      * @param {Roo.data.Store} store The data store to bind
13272      */
13273     bind : function(ds){
13274         ds.on("beforeload", this.beforeLoad, this);
13275         ds.on("load", this.onLoad, this);
13276         ds.on("loadexception", this.onLoadError, this);
13277         ds.on("remove", this.updateInfo, this);
13278         ds.on("add", this.updateInfo, this);
13279         this.ds = ds;
13280     }
13281 });/*
13282  * Based on:
13283  * Ext JS Library 1.1.1
13284  * Copyright(c) 2006-2007, Ext JS, LLC.
13285  *
13286  * Originally Released Under LGPL - original licence link has changed is not relivant.
13287  *
13288  * Fork - LGPL
13289  * <script type="text/javascript">
13290  */
13291
13292 /**
13293  * @class Roo.Resizable
13294  * @extends Roo.util.Observable
13295  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
13296  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
13297  * 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
13298  * the element will be wrapped for you automatically.</p>
13299  * <p>Here is the list of valid resize handles:</p>
13300  * <pre>
13301 Value   Description
13302 ------  -------------------
13303  'n'     north
13304  's'     south
13305  'e'     east
13306  'w'     west
13307  'nw'    northwest
13308  'sw'    southwest
13309  'se'    southeast
13310  'ne'    northeast
13311  'hd'    horizontal drag
13312  'all'   all
13313 </pre>
13314  * <p>Here's an example showing the creation of a typical Resizable:</p>
13315  * <pre><code>
13316 var resizer = new Roo.Resizable("element-id", {
13317     handles: 'all',
13318     minWidth: 200,
13319     minHeight: 100,
13320     maxWidth: 500,
13321     maxHeight: 400,
13322     pinned: true
13323 });
13324 resizer.on("resize", myHandler);
13325 </code></pre>
13326  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
13327  * resizer.east.setDisplayed(false);</p>
13328  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
13329  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
13330  * resize operation's new size (defaults to [0, 0])
13331  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
13332  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
13333  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
13334  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
13335  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
13336  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
13337  * @cfg {Number} width The width of the element in pixels (defaults to null)
13338  * @cfg {Number} height The height of the element in pixels (defaults to null)
13339  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
13340  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
13341  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
13342  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
13343  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
13344  * in favor of the handles config option (defaults to false)
13345  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
13346  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
13347  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
13348  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
13349  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
13350  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
13351  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
13352  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
13353  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
13354  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
13355  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
13356  * @constructor
13357  * Create a new resizable component
13358  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
13359  * @param {Object} config configuration options
13360   */
13361 Roo.Resizable = function(el, config)
13362 {
13363     this.el = Roo.get(el);
13364
13365     if(config && config.wrap){
13366         config.resizeChild = this.el;
13367         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
13368         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
13369         this.el.setStyle("overflow", "hidden");
13370         this.el.setPositioning(config.resizeChild.getPositioning());
13371         config.resizeChild.clearPositioning();
13372         if(!config.width || !config.height){
13373             var csize = config.resizeChild.getSize();
13374             this.el.setSize(csize.width, csize.height);
13375         }
13376         if(config.pinned && !config.adjustments){
13377             config.adjustments = "auto";
13378         }
13379     }
13380
13381     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
13382     this.proxy.unselectable();
13383     this.proxy.enableDisplayMode('block');
13384
13385     Roo.apply(this, config);
13386
13387     if(this.pinned){
13388         this.disableTrackOver = true;
13389         this.el.addClass("x-resizable-pinned");
13390     }
13391     // if the element isn't positioned, make it relative
13392     var position = this.el.getStyle("position");
13393     if(position != "absolute" && position != "fixed"){
13394         this.el.setStyle("position", "relative");
13395     }
13396     if(!this.handles){ // no handles passed, must be legacy style
13397         this.handles = 's,e,se';
13398         if(this.multiDirectional){
13399             this.handles += ',n,w';
13400         }
13401     }
13402     if(this.handles == "all"){
13403         this.handles = "n s e w ne nw se sw";
13404     }
13405     var hs = this.handles.split(/\s*?[,;]\s*?| /);
13406     var ps = Roo.Resizable.positions;
13407     for(var i = 0, len = hs.length; i < len; i++){
13408         if(hs[i] && ps[hs[i]]){
13409             var pos = ps[hs[i]];
13410             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
13411         }
13412     }
13413     // legacy
13414     this.corner = this.southeast;
13415     
13416     // updateBox = the box can move..
13417     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
13418         this.updateBox = true;
13419     }
13420
13421     this.activeHandle = null;
13422
13423     if(this.resizeChild){
13424         if(typeof this.resizeChild == "boolean"){
13425             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
13426         }else{
13427             this.resizeChild = Roo.get(this.resizeChild, true);
13428         }
13429     }
13430     
13431     if(this.adjustments == "auto"){
13432         var rc = this.resizeChild;
13433         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
13434         if(rc && (hw || hn)){
13435             rc.position("relative");
13436             rc.setLeft(hw ? hw.el.getWidth() : 0);
13437             rc.setTop(hn ? hn.el.getHeight() : 0);
13438         }
13439         this.adjustments = [
13440             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
13441             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
13442         ];
13443     }
13444
13445     if(this.draggable){
13446         this.dd = this.dynamic ?
13447             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
13448         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
13449     }
13450
13451     // public events
13452     this.addEvents({
13453         /**
13454          * @event beforeresize
13455          * Fired before resize is allowed. Set enabled to false to cancel resize.
13456          * @param {Roo.Resizable} this
13457          * @param {Roo.EventObject} e The mousedown event
13458          */
13459         "beforeresize" : true,
13460         /**
13461          * @event resize
13462          * Fired after a resize.
13463          * @param {Roo.Resizable} this
13464          * @param {Number} width The new width
13465          * @param {Number} height The new height
13466          * @param {Roo.EventObject} e The mouseup event
13467          */
13468         "resize" : true
13469     });
13470
13471     if(this.width !== null && this.height !== null){
13472         this.resizeTo(this.width, this.height);
13473     }else{
13474         this.updateChildSize();
13475     }
13476     if(Roo.isIE){
13477         this.el.dom.style.zoom = 1;
13478     }
13479     Roo.Resizable.superclass.constructor.call(this);
13480 };
13481
13482 Roo.extend(Roo.Resizable, Roo.util.Observable, {
13483         resizeChild : false,
13484         adjustments : [0, 0],
13485         minWidth : 5,
13486         minHeight : 5,
13487         maxWidth : 10000,
13488         maxHeight : 10000,
13489         enabled : true,
13490         animate : false,
13491         duration : .35,
13492         dynamic : false,
13493         handles : false,
13494         multiDirectional : false,
13495         disableTrackOver : false,
13496         easing : 'easeOutStrong',
13497         widthIncrement : 0,
13498         heightIncrement : 0,
13499         pinned : false,
13500         width : null,
13501         height : null,
13502         preserveRatio : false,
13503         transparent: false,
13504         minX: 0,
13505         minY: 0,
13506         draggable: false,
13507
13508         /**
13509          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
13510          */
13511         constrainTo: undefined,
13512         /**
13513          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
13514          */
13515         resizeRegion: undefined,
13516
13517
13518     /**
13519      * Perform a manual resize
13520      * @param {Number} width
13521      * @param {Number} height
13522      */
13523     resizeTo : function(width, height){
13524         this.el.setSize(width, height);
13525         this.updateChildSize();
13526         this.fireEvent("resize", this, width, height, null);
13527     },
13528
13529     // private
13530     startSizing : function(e, handle){
13531         this.fireEvent("beforeresize", this, e);
13532         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
13533
13534             if(!this.overlay){
13535                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
13536                 this.overlay.unselectable();
13537                 this.overlay.enableDisplayMode("block");
13538                 this.overlay.on("mousemove", this.onMouseMove, this);
13539                 this.overlay.on("mouseup", this.onMouseUp, this);
13540             }
13541             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
13542
13543             this.resizing = true;
13544             this.startBox = this.el.getBox();
13545             this.startPoint = e.getXY();
13546             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
13547                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
13548
13549             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13550             this.overlay.show();
13551
13552             if(this.constrainTo) {
13553                 var ct = Roo.get(this.constrainTo);
13554                 this.resizeRegion = ct.getRegion().adjust(
13555                     ct.getFrameWidth('t'),
13556                     ct.getFrameWidth('l'),
13557                     -ct.getFrameWidth('b'),
13558                     -ct.getFrameWidth('r')
13559                 );
13560             }
13561
13562             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
13563             this.proxy.show();
13564             this.proxy.setBox(this.startBox);
13565             if(!this.dynamic){
13566                 this.proxy.setStyle('visibility', 'visible');
13567             }
13568         }
13569     },
13570
13571     // private
13572     onMouseDown : function(handle, e){
13573         if(this.enabled){
13574             e.stopEvent();
13575             this.activeHandle = handle;
13576             this.startSizing(e, handle);
13577         }
13578     },
13579
13580     // private
13581     onMouseUp : function(e){
13582         var size = this.resizeElement();
13583         this.resizing = false;
13584         this.handleOut();
13585         this.overlay.hide();
13586         this.proxy.hide();
13587         this.fireEvent("resize", this, size.width, size.height, e);
13588     },
13589
13590     // private
13591     updateChildSize : function(){
13592         if(this.resizeChild){
13593             var el = this.el;
13594             var child = this.resizeChild;
13595             var adj = this.adjustments;
13596             if(el.dom.offsetWidth){
13597                 var b = el.getSize(true);
13598                 child.setSize(b.width+adj[0], b.height+adj[1]);
13599             }
13600             // Second call here for IE
13601             // The first call enables instant resizing and
13602             // the second call corrects scroll bars if they
13603             // exist
13604             if(Roo.isIE){
13605                 setTimeout(function(){
13606                     if(el.dom.offsetWidth){
13607                         var b = el.getSize(true);
13608                         child.setSize(b.width+adj[0], b.height+adj[1]);
13609                     }
13610                 }, 10);
13611             }
13612         }
13613     },
13614
13615     // private
13616     snap : function(value, inc, min){
13617         if(!inc || !value) return value;
13618         var newValue = value;
13619         var m = value % inc;
13620         if(m > 0){
13621             if(m > (inc/2)){
13622                 newValue = value + (inc-m);
13623             }else{
13624                 newValue = value - m;
13625             }
13626         }
13627         return Math.max(min, newValue);
13628     },
13629
13630     // private
13631     resizeElement : function(){
13632         var box = this.proxy.getBox();
13633         if(this.updateBox){
13634             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13635         }else{
13636             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13637         }
13638         this.updateChildSize();
13639         if(!this.dynamic){
13640             this.proxy.hide();
13641         }
13642         return box;
13643     },
13644
13645     // private
13646     constrain : function(v, diff, m, mx){
13647         if(v - diff < m){
13648             diff = v - m;
13649         }else if(v - diff > mx){
13650             diff = mx - v;
13651         }
13652         return diff;
13653     },
13654
13655     // private
13656     onMouseMove : function(e){
13657         if(this.enabled){
13658             try{// try catch so if something goes wrong the user doesn't get hung
13659
13660             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13661                 return;
13662             }
13663
13664             //var curXY = this.startPoint;
13665             var curSize = this.curSize || this.startBox;
13666             var x = this.startBox.x, y = this.startBox.y;
13667             var ox = x, oy = y;
13668             var w = curSize.width, h = curSize.height;
13669             var ow = w, oh = h;
13670             var mw = this.minWidth, mh = this.minHeight;
13671             var mxw = this.maxWidth, mxh = this.maxHeight;
13672             var wi = this.widthIncrement;
13673             var hi = this.heightIncrement;
13674
13675             var eventXY = e.getXY();
13676             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13677             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13678
13679             var pos = this.activeHandle.position;
13680
13681             switch(pos){
13682                 case "east":
13683                     w += diffX;
13684                     w = Math.min(Math.max(mw, w), mxw);
13685                     break;
13686              
13687                 case "south":
13688                     h += diffY;
13689                     h = Math.min(Math.max(mh, h), mxh);
13690                     break;
13691                 case "southeast":
13692                     w += diffX;
13693                     h += diffY;
13694                     w = Math.min(Math.max(mw, w), mxw);
13695                     h = Math.min(Math.max(mh, h), mxh);
13696                     break;
13697                 case "north":
13698                     diffY = this.constrain(h, diffY, mh, mxh);
13699                     y += diffY;
13700                     h -= diffY;
13701                     break;
13702                 case "hdrag":
13703                     
13704                     if (wi) {
13705                         var adiffX = Math.abs(diffX);
13706                         var sub = (adiffX % wi); // how much 
13707                         if (sub > (wi/2)) { // far enough to snap
13708                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
13709                         } else {
13710                             // remove difference.. 
13711                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
13712                         }
13713                     }
13714                     x += diffX;
13715                     x = Math.max(this.minX, x);
13716                     break;
13717                 case "west":
13718                     diffX = this.constrain(w, diffX, mw, mxw);
13719                     x += diffX;
13720                     w -= diffX;
13721                     break;
13722                 case "northeast":
13723                     w += diffX;
13724                     w = Math.min(Math.max(mw, w), mxw);
13725                     diffY = this.constrain(h, diffY, mh, mxh);
13726                     y += diffY;
13727                     h -= diffY;
13728                     break;
13729                 case "northwest":
13730                     diffX = this.constrain(w, diffX, mw, mxw);
13731                     diffY = this.constrain(h, diffY, mh, mxh);
13732                     y += diffY;
13733                     h -= diffY;
13734                     x += diffX;
13735                     w -= diffX;
13736                     break;
13737                case "southwest":
13738                     diffX = this.constrain(w, diffX, mw, mxw);
13739                     h += diffY;
13740                     h = Math.min(Math.max(mh, h), mxh);
13741                     x += diffX;
13742                     w -= diffX;
13743                     break;
13744             }
13745
13746             var sw = this.snap(w, wi, mw);
13747             var sh = this.snap(h, hi, mh);
13748             if(sw != w || sh != h){
13749                 switch(pos){
13750                     case "northeast":
13751                         y -= sh - h;
13752                     break;
13753                     case "north":
13754                         y -= sh - h;
13755                         break;
13756                     case "southwest":
13757                         x -= sw - w;
13758                     break;
13759                     case "west":
13760                         x -= sw - w;
13761                         break;
13762                     case "northwest":
13763                         x -= sw - w;
13764                         y -= sh - h;
13765                     break;
13766                 }
13767                 w = sw;
13768                 h = sh;
13769             }
13770
13771             if(this.preserveRatio){
13772                 switch(pos){
13773                     case "southeast":
13774                     case "east":
13775                         h = oh * (w/ow);
13776                         h = Math.min(Math.max(mh, h), mxh);
13777                         w = ow * (h/oh);
13778                        break;
13779                     case "south":
13780                         w = ow * (h/oh);
13781                         w = Math.min(Math.max(mw, w), mxw);
13782                         h = oh * (w/ow);
13783                         break;
13784                     case "northeast":
13785                         w = ow * (h/oh);
13786                         w = Math.min(Math.max(mw, w), mxw);
13787                         h = oh * (w/ow);
13788                     break;
13789                     case "north":
13790                         var tw = w;
13791                         w = ow * (h/oh);
13792                         w = Math.min(Math.max(mw, w), mxw);
13793                         h = oh * (w/ow);
13794                         x += (tw - w) / 2;
13795                         break;
13796                     case "southwest":
13797                         h = oh * (w/ow);
13798                         h = Math.min(Math.max(mh, h), mxh);
13799                         var tw = w;
13800                         w = ow * (h/oh);
13801                         x += tw - w;
13802                         break;
13803                     case "west":
13804                         var th = h;
13805                         h = oh * (w/ow);
13806                         h = Math.min(Math.max(mh, h), mxh);
13807                         y += (th - h) / 2;
13808                         var tw = w;
13809                         w = ow * (h/oh);
13810                         x += tw - w;
13811                        break;
13812                     case "northwest":
13813                         var tw = w;
13814                         var th = h;
13815                         h = oh * (w/ow);
13816                         h = Math.min(Math.max(mh, h), mxh);
13817                         w = ow * (h/oh);
13818                         y += th - h;
13819                         x += tw - w;
13820                        break;
13821
13822                 }
13823             }
13824             if (pos == 'hdrag') {
13825                 w = ow;
13826             }
13827             this.proxy.setBounds(x, y, w, h);
13828             if(this.dynamic){
13829                 this.resizeElement();
13830             }
13831             }catch(e){}
13832         }
13833     },
13834
13835     // private
13836     handleOver : function(){
13837         if(this.enabled){
13838             this.el.addClass("x-resizable-over");
13839         }
13840     },
13841
13842     // private
13843     handleOut : function(){
13844         if(!this.resizing){
13845             this.el.removeClass("x-resizable-over");
13846         }
13847     },
13848
13849     /**
13850      * Returns the element this component is bound to.
13851      * @return {Roo.Element}
13852      */
13853     getEl : function(){
13854         return this.el;
13855     },
13856
13857     /**
13858      * Returns the resizeChild element (or null).
13859      * @return {Roo.Element}
13860      */
13861     getResizeChild : function(){
13862         return this.resizeChild;
13863     },
13864
13865     /**
13866      * Destroys this resizable. If the element was wrapped and
13867      * removeEl is not true then the element remains.
13868      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13869      */
13870     destroy : function(removeEl){
13871         this.proxy.remove();
13872         if(this.overlay){
13873             this.overlay.removeAllListeners();
13874             this.overlay.remove();
13875         }
13876         var ps = Roo.Resizable.positions;
13877         for(var k in ps){
13878             if(typeof ps[k] != "function" && this[ps[k]]){
13879                 var h = this[ps[k]];
13880                 h.el.removeAllListeners();
13881                 h.el.remove();
13882             }
13883         }
13884         if(removeEl){
13885             this.el.update("");
13886             this.el.remove();
13887         }
13888     }
13889 });
13890
13891 // private
13892 // hash to map config positions to true positions
13893 Roo.Resizable.positions = {
13894     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
13895     hd: "hdrag"
13896 };
13897
13898 // private
13899 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
13900     if(!this.tpl){
13901         // only initialize the template if resizable is used
13902         var tpl = Roo.DomHelper.createTemplate(
13903             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
13904         );
13905         tpl.compile();
13906         Roo.Resizable.Handle.prototype.tpl = tpl;
13907     }
13908     this.position = pos;
13909     this.rz = rz;
13910     // show north drag fro topdra
13911     var handlepos = pos == 'hdrag' ? 'north' : pos;
13912     
13913     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
13914     if (pos == 'hdrag') {
13915         this.el.setStyle('cursor', 'pointer');
13916     }
13917     this.el.unselectable();
13918     if(transparent){
13919         this.el.setOpacity(0);
13920     }
13921     this.el.on("mousedown", this.onMouseDown, this);
13922     if(!disableTrackOver){
13923         this.el.on("mouseover", this.onMouseOver, this);
13924         this.el.on("mouseout", this.onMouseOut, this);
13925     }
13926 };
13927
13928 // private
13929 Roo.Resizable.Handle.prototype = {
13930     afterResize : function(rz){
13931         // do nothing
13932     },
13933     // private
13934     onMouseDown : function(e){
13935         this.rz.onMouseDown(this, e);
13936     },
13937     // private
13938     onMouseOver : function(e){
13939         this.rz.handleOver(this, e);
13940     },
13941     // private
13942     onMouseOut : function(e){
13943         this.rz.handleOut(this, e);
13944     }
13945 };/*
13946  * Based on:
13947  * Ext JS Library 1.1.1
13948  * Copyright(c) 2006-2007, Ext JS, LLC.
13949  *
13950  * Originally Released Under LGPL - original licence link has changed is not relivant.
13951  *
13952  * Fork - LGPL
13953  * <script type="text/javascript">
13954  */
13955
13956 /**
13957  * @class Roo.Editor
13958  * @extends Roo.Component
13959  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
13960  * @constructor
13961  * Create a new Editor
13962  * @param {Roo.form.Field} field The Field object (or descendant)
13963  * @param {Object} config The config object
13964  */
13965 Roo.Editor = function(field, config){
13966     Roo.Editor.superclass.constructor.call(this, config);
13967     this.field = field;
13968     this.addEvents({
13969         /**
13970              * @event beforestartedit
13971              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
13972              * false from the handler of this event.
13973              * @param {Editor} this
13974              * @param {Roo.Element} boundEl The underlying element bound to this editor
13975              * @param {Mixed} value The field value being set
13976              */
13977         "beforestartedit" : true,
13978         /**
13979              * @event startedit
13980              * Fires when this editor is displayed
13981              * @param {Roo.Element} boundEl The underlying element bound to this editor
13982              * @param {Mixed} value The starting field value
13983              */
13984         "startedit" : true,
13985         /**
13986              * @event beforecomplete
13987              * Fires after a change has been made to the field, but before the change is reflected in the underlying
13988              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
13989              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
13990              * event will not fire since no edit actually occurred.
13991              * @param {Editor} this
13992              * @param {Mixed} value The current field value
13993              * @param {Mixed} startValue The original field value
13994              */
13995         "beforecomplete" : true,
13996         /**
13997              * @event complete
13998              * Fires after editing is complete and any changed value has been written to the underlying field.
13999              * @param {Editor} this
14000              * @param {Mixed} value The current field value
14001              * @param {Mixed} startValue The original field value
14002              */
14003         "complete" : true,
14004         /**
14005          * @event specialkey
14006          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
14007          * {@link Roo.EventObject#getKey} to determine which key was pressed.
14008          * @param {Roo.form.Field} this
14009          * @param {Roo.EventObject} e The event object
14010          */
14011         "specialkey" : true
14012     });
14013 };
14014
14015 Roo.extend(Roo.Editor, Roo.Component, {
14016     /**
14017      * @cfg {Boolean/String} autosize
14018      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
14019      * or "height" to adopt the height only (defaults to false)
14020      */
14021     /**
14022      * @cfg {Boolean} revertInvalid
14023      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
14024      * validation fails (defaults to true)
14025      */
14026     /**
14027      * @cfg {Boolean} ignoreNoChange
14028      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
14029      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
14030      * will never be ignored.
14031      */
14032     /**
14033      * @cfg {Boolean} hideEl
14034      * False to keep the bound element visible while the editor is displayed (defaults to true)
14035      */
14036     /**
14037      * @cfg {Mixed} value
14038      * The data value of the underlying field (defaults to "")
14039      */
14040     value : "",
14041     /**
14042      * @cfg {String} alignment
14043      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
14044      */
14045     alignment: "c-c?",
14046     /**
14047      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
14048      * for bottom-right shadow (defaults to "frame")
14049      */
14050     shadow : "frame",
14051     /**
14052      * @cfg {Boolean} constrain True to constrain the editor to the viewport
14053      */
14054     constrain : false,
14055     /**
14056      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
14057      */
14058     completeOnEnter : false,
14059     /**
14060      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
14061      */
14062     cancelOnEsc : false,
14063     /**
14064      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
14065      */
14066     updateEl : false,
14067
14068     // private
14069     onRender : function(ct, position){
14070         this.el = new Roo.Layer({
14071             shadow: this.shadow,
14072             cls: "x-editor",
14073             parentEl : ct,
14074             shim : this.shim,
14075             shadowOffset:4,
14076             id: this.id,
14077             constrain: this.constrain
14078         });
14079         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
14080         if(this.field.msgTarget != 'title'){
14081             this.field.msgTarget = 'qtip';
14082         }
14083         this.field.render(this.el);
14084         if(Roo.isGecko){
14085             this.field.el.dom.setAttribute('autocomplete', 'off');
14086         }
14087         this.field.on("specialkey", this.onSpecialKey, this);
14088         if(this.swallowKeys){
14089             this.field.el.swallowEvent(['keydown','keypress']);
14090         }
14091         this.field.show();
14092         this.field.on("blur", this.onBlur, this);
14093         if(this.field.grow){
14094             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
14095         }
14096     },
14097
14098     onSpecialKey : function(field, e)
14099     {
14100         //Roo.log('editor onSpecialKey');
14101         if(this.completeOnEnter && e.getKey() == e.ENTER){
14102             e.stopEvent();
14103             this.completeEdit();
14104             return;
14105         }
14106         // do not fire special key otherwise it might hide close the editor...
14107         if(e.getKey() == e.ENTER){    
14108             return;
14109         }
14110         if(this.cancelOnEsc && e.getKey() == e.ESC){
14111             this.cancelEdit();
14112             return;
14113         } 
14114         this.fireEvent('specialkey', field, e);
14115     
14116     },
14117
14118     /**
14119      * Starts the editing process and shows the editor.
14120      * @param {String/HTMLElement/Element} el The element to edit
14121      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
14122       * to the innerHTML of el.
14123      */
14124     startEdit : function(el, value){
14125         if(this.editing){
14126             this.completeEdit();
14127         }
14128         this.boundEl = Roo.get(el);
14129         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
14130         if(!this.rendered){
14131             this.render(this.parentEl || document.body);
14132         }
14133         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
14134             return;
14135         }
14136         this.startValue = v;
14137         this.field.setValue(v);
14138         if(this.autoSize){
14139             var sz = this.boundEl.getSize();
14140             switch(this.autoSize){
14141                 case "width":
14142                 this.setSize(sz.width,  "");
14143                 break;
14144                 case "height":
14145                 this.setSize("",  sz.height);
14146                 break;
14147                 default:
14148                 this.setSize(sz.width,  sz.height);
14149             }
14150         }
14151         this.el.alignTo(this.boundEl, this.alignment);
14152         this.editing = true;
14153         if(Roo.QuickTips){
14154             Roo.QuickTips.disable();
14155         }
14156         this.show();
14157     },
14158
14159     /**
14160      * Sets the height and width of this editor.
14161      * @param {Number} width The new width
14162      * @param {Number} height The new height
14163      */
14164     setSize : function(w, h){
14165         this.field.setSize(w, h);
14166         if(this.el){
14167             this.el.sync();
14168         }
14169     },
14170
14171     /**
14172      * Realigns the editor to the bound field based on the current alignment config value.
14173      */
14174     realign : function(){
14175         this.el.alignTo(this.boundEl, this.alignment);
14176     },
14177
14178     /**
14179      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
14180      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
14181      */
14182     completeEdit : function(remainVisible){
14183         if(!this.editing){
14184             return;
14185         }
14186         var v = this.getValue();
14187         if(this.revertInvalid !== false && !this.field.isValid()){
14188             v = this.startValue;
14189             this.cancelEdit(true);
14190         }
14191         if(String(v) === String(this.startValue) && this.ignoreNoChange){
14192             this.editing = false;
14193             this.hide();
14194             return;
14195         }
14196         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
14197             this.editing = false;
14198             if(this.updateEl && this.boundEl){
14199                 this.boundEl.update(v);
14200             }
14201             if(remainVisible !== true){
14202                 this.hide();
14203             }
14204             this.fireEvent("complete", this, v, this.startValue);
14205         }
14206     },
14207
14208     // private
14209     onShow : function(){
14210         this.el.show();
14211         if(this.hideEl !== false){
14212             this.boundEl.hide();
14213         }
14214         this.field.show();
14215         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
14216             this.fixIEFocus = true;
14217             this.deferredFocus.defer(50, this);
14218         }else{
14219             this.field.focus();
14220         }
14221         this.fireEvent("startedit", this.boundEl, this.startValue);
14222     },
14223
14224     deferredFocus : function(){
14225         if(this.editing){
14226             this.field.focus();
14227         }
14228     },
14229
14230     /**
14231      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
14232      * reverted to the original starting value.
14233      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
14234      * cancel (defaults to false)
14235      */
14236     cancelEdit : function(remainVisible){
14237         if(this.editing){
14238             this.setValue(this.startValue);
14239             if(remainVisible !== true){
14240                 this.hide();
14241             }
14242         }
14243     },
14244
14245     // private
14246     onBlur : function(){
14247         if(this.allowBlur !== true && this.editing){
14248             this.completeEdit();
14249         }
14250     },
14251
14252     // private
14253     onHide : function(){
14254         if(this.editing){
14255             this.completeEdit();
14256             return;
14257         }
14258         this.field.blur();
14259         if(this.field.collapse){
14260             this.field.collapse();
14261         }
14262         this.el.hide();
14263         if(this.hideEl !== false){
14264             this.boundEl.show();
14265         }
14266         if(Roo.QuickTips){
14267             Roo.QuickTips.enable();
14268         }
14269     },
14270
14271     /**
14272      * Sets the data value of the editor
14273      * @param {Mixed} value Any valid value supported by the underlying field
14274      */
14275     setValue : function(v){
14276         this.field.setValue(v);
14277     },
14278
14279     /**
14280      * Gets the data value of the editor
14281      * @return {Mixed} The data value
14282      */
14283     getValue : function(){
14284         return this.field.getValue();
14285     }
14286 });/*
14287  * Based on:
14288  * Ext JS Library 1.1.1
14289  * Copyright(c) 2006-2007, Ext JS, LLC.
14290  *
14291  * Originally Released Under LGPL - original licence link has changed is not relivant.
14292  *
14293  * Fork - LGPL
14294  * <script type="text/javascript">
14295  */
14296  
14297 /**
14298  * @class Roo.BasicDialog
14299  * @extends Roo.util.Observable
14300  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
14301  * <pre><code>
14302 var dlg = new Roo.BasicDialog("my-dlg", {
14303     height: 200,
14304     width: 300,
14305     minHeight: 100,
14306     minWidth: 150,
14307     modal: true,
14308     proxyDrag: true,
14309     shadow: true
14310 });
14311 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
14312 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
14313 dlg.addButton('Cancel', dlg.hide, dlg);
14314 dlg.show();
14315 </code></pre>
14316   <b>A Dialog should always be a direct child of the body element.</b>
14317  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
14318  * @cfg {String} title Default text to display in the title bar (defaults to null)
14319  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14320  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14321  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
14322  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
14323  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
14324  * (defaults to null with no animation)
14325  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
14326  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
14327  * property for valid values (defaults to 'all')
14328  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
14329  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
14330  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
14331  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
14332  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
14333  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
14334  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
14335  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
14336  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
14337  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
14338  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
14339  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
14340  * draggable = true (defaults to false)
14341  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
14342  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14343  * shadow (defaults to false)
14344  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
14345  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
14346  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
14347  * @cfg {Array} buttons Array of buttons
14348  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
14349  * @constructor
14350  * Create a new BasicDialog.
14351  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
14352  * @param {Object} config Configuration options
14353  */
14354 Roo.BasicDialog = function(el, config){
14355     this.el = Roo.get(el);
14356     var dh = Roo.DomHelper;
14357     if(!this.el && config && config.autoCreate){
14358         if(typeof config.autoCreate == "object"){
14359             if(!config.autoCreate.id){
14360                 config.autoCreate.id = el;
14361             }
14362             this.el = dh.append(document.body,
14363                         config.autoCreate, true);
14364         }else{
14365             this.el = dh.append(document.body,
14366                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
14367         }
14368     }
14369     el = this.el;
14370     el.setDisplayed(true);
14371     el.hide = this.hideAction;
14372     this.id = el.id;
14373     el.addClass("x-dlg");
14374
14375     Roo.apply(this, config);
14376
14377     this.proxy = el.createProxy("x-dlg-proxy");
14378     this.proxy.hide = this.hideAction;
14379     this.proxy.setOpacity(.5);
14380     this.proxy.hide();
14381
14382     if(config.width){
14383         el.setWidth(config.width);
14384     }
14385     if(config.height){
14386         el.setHeight(config.height);
14387     }
14388     this.size = el.getSize();
14389     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
14390         this.xy = [config.x,config.y];
14391     }else{
14392         this.xy = el.getCenterXY(true);
14393     }
14394     /** The header element @type Roo.Element */
14395     this.header = el.child("> .x-dlg-hd");
14396     /** The body element @type Roo.Element */
14397     this.body = el.child("> .x-dlg-bd");
14398     /** The footer element @type Roo.Element */
14399     this.footer = el.child("> .x-dlg-ft");
14400
14401     if(!this.header){
14402         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
14403     }
14404     if(!this.body){
14405         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
14406     }
14407
14408     this.header.unselectable();
14409     if(this.title){
14410         this.header.update(this.title);
14411     }
14412     // this element allows the dialog to be focused for keyboard event
14413     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
14414     this.focusEl.swallowEvent("click", true);
14415
14416     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
14417
14418     // wrap the body and footer for special rendering
14419     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
14420     if(this.footer){
14421         this.bwrap.dom.appendChild(this.footer.dom);
14422     }
14423
14424     this.bg = this.el.createChild({
14425         tag: "div", cls:"x-dlg-bg",
14426         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
14427     });
14428     this.centerBg = this.bg.child("div.x-dlg-bg-center");
14429
14430
14431     if(this.autoScroll !== false && !this.autoTabs){
14432         this.body.setStyle("overflow", "auto");
14433     }
14434
14435     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
14436
14437     if(this.closable !== false){
14438         this.el.addClass("x-dlg-closable");
14439         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
14440         this.close.on("click", this.closeClick, this);
14441         this.close.addClassOnOver("x-dlg-close-over");
14442     }
14443     if(this.collapsible !== false){
14444         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
14445         this.collapseBtn.on("click", this.collapseClick, this);
14446         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
14447         this.header.on("dblclick", this.collapseClick, this);
14448     }
14449     if(this.resizable !== false){
14450         this.el.addClass("x-dlg-resizable");
14451         this.resizer = new Roo.Resizable(el, {
14452             minWidth: this.minWidth || 80,
14453             minHeight:this.minHeight || 80,
14454             handles: this.resizeHandles || "all",
14455             pinned: true
14456         });
14457         this.resizer.on("beforeresize", this.beforeResize, this);
14458         this.resizer.on("resize", this.onResize, this);
14459     }
14460     if(this.draggable !== false){
14461         el.addClass("x-dlg-draggable");
14462         if (!this.proxyDrag) {
14463             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
14464         }
14465         else {
14466             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
14467         }
14468         dd.setHandleElId(this.header.id);
14469         dd.endDrag = this.endMove.createDelegate(this);
14470         dd.startDrag = this.startMove.createDelegate(this);
14471         dd.onDrag = this.onDrag.createDelegate(this);
14472         dd.scroll = false;
14473         this.dd = dd;
14474     }
14475     if(this.modal){
14476         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
14477         this.mask.enableDisplayMode("block");
14478         this.mask.hide();
14479         this.el.addClass("x-dlg-modal");
14480     }
14481     if(this.shadow){
14482         this.shadow = new Roo.Shadow({
14483             mode : typeof this.shadow == "string" ? this.shadow : "sides",
14484             offset : this.shadowOffset
14485         });
14486     }else{
14487         this.shadowOffset = 0;
14488     }
14489     if(Roo.useShims && this.shim !== false){
14490         this.shim = this.el.createShim();
14491         this.shim.hide = this.hideAction;
14492         this.shim.hide();
14493     }else{
14494         this.shim = false;
14495     }
14496     if(this.autoTabs){
14497         this.initTabs();
14498     }
14499     if (this.buttons) { 
14500         var bts= this.buttons;
14501         this.buttons = [];
14502         Roo.each(bts, function(b) {
14503             this.addButton(b);
14504         }, this);
14505     }
14506     
14507     
14508     this.addEvents({
14509         /**
14510          * @event keydown
14511          * Fires when a key is pressed
14512          * @param {Roo.BasicDialog} this
14513          * @param {Roo.EventObject} e
14514          */
14515         "keydown" : true,
14516         /**
14517          * @event move
14518          * Fires when this dialog is moved by the user.
14519          * @param {Roo.BasicDialog} this
14520          * @param {Number} x The new page X
14521          * @param {Number} y The new page Y
14522          */
14523         "move" : true,
14524         /**
14525          * @event resize
14526          * Fires when this dialog is resized by the user.
14527          * @param {Roo.BasicDialog} this
14528          * @param {Number} width The new width
14529          * @param {Number} height The new height
14530          */
14531         "resize" : true,
14532         /**
14533          * @event beforehide
14534          * Fires before this dialog is hidden.
14535          * @param {Roo.BasicDialog} this
14536          */
14537         "beforehide" : true,
14538         /**
14539          * @event hide
14540          * Fires when this dialog is hidden.
14541          * @param {Roo.BasicDialog} this
14542          */
14543         "hide" : true,
14544         /**
14545          * @event beforeshow
14546          * Fires before this dialog is shown.
14547          * @param {Roo.BasicDialog} this
14548          */
14549         "beforeshow" : true,
14550         /**
14551          * @event show
14552          * Fires when this dialog is shown.
14553          * @param {Roo.BasicDialog} this
14554          */
14555         "show" : true
14556     });
14557     el.on("keydown", this.onKeyDown, this);
14558     el.on("mousedown", this.toFront, this);
14559     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
14560     this.el.hide();
14561     Roo.DialogManager.register(this);
14562     Roo.BasicDialog.superclass.constructor.call(this);
14563 };
14564
14565 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
14566     shadowOffset: Roo.isIE ? 6 : 5,
14567     minHeight: 80,
14568     minWidth: 200,
14569     minButtonWidth: 75,
14570     defaultButton: null,
14571     buttonAlign: "right",
14572     tabTag: 'div',
14573     firstShow: true,
14574
14575     /**
14576      * Sets the dialog title text
14577      * @param {String} text The title text to display
14578      * @return {Roo.BasicDialog} this
14579      */
14580     setTitle : function(text){
14581         this.header.update(text);
14582         return this;
14583     },
14584
14585     // private
14586     closeClick : function(){
14587         this.hide();
14588     },
14589
14590     // private
14591     collapseClick : function(){
14592         this[this.collapsed ? "expand" : "collapse"]();
14593     },
14594
14595     /**
14596      * Collapses the dialog to its minimized state (only the title bar is visible).
14597      * Equivalent to the user clicking the collapse dialog button.
14598      */
14599     collapse : function(){
14600         if(!this.collapsed){
14601             this.collapsed = true;
14602             this.el.addClass("x-dlg-collapsed");
14603             this.restoreHeight = this.el.getHeight();
14604             this.resizeTo(this.el.getWidth(), this.header.getHeight());
14605         }
14606     },
14607
14608     /**
14609      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
14610      * clicking the expand dialog button.
14611      */
14612     expand : function(){
14613         if(this.collapsed){
14614             this.collapsed = false;
14615             this.el.removeClass("x-dlg-collapsed");
14616             this.resizeTo(this.el.getWidth(), this.restoreHeight);
14617         }
14618     },
14619
14620     /**
14621      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14622      * @return {Roo.TabPanel} The tabs component
14623      */
14624     initTabs : function(){
14625         var tabs = this.getTabs();
14626         while(tabs.getTab(0)){
14627             tabs.removeTab(0);
14628         }
14629         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14630             var dom = el.dom;
14631             tabs.addTab(Roo.id(dom), dom.title);
14632             dom.title = "";
14633         });
14634         tabs.activate(0);
14635         return tabs;
14636     },
14637
14638     // private
14639     beforeResize : function(){
14640         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14641     },
14642
14643     // private
14644     onResize : function(){
14645         this.refreshSize();
14646         this.syncBodyHeight();
14647         this.adjustAssets();
14648         this.focus();
14649         this.fireEvent("resize", this, this.size.width, this.size.height);
14650     },
14651
14652     // private
14653     onKeyDown : function(e){
14654         if(this.isVisible()){
14655             this.fireEvent("keydown", this, e);
14656         }
14657     },
14658
14659     /**
14660      * Resizes the dialog.
14661      * @param {Number} width
14662      * @param {Number} height
14663      * @return {Roo.BasicDialog} this
14664      */
14665     resizeTo : function(width, height){
14666         this.el.setSize(width, height);
14667         this.size = {width: width, height: height};
14668         this.syncBodyHeight();
14669         if(this.fixedcenter){
14670             this.center();
14671         }
14672         if(this.isVisible()){
14673             this.constrainXY();
14674             this.adjustAssets();
14675         }
14676         this.fireEvent("resize", this, width, height);
14677         return this;
14678     },
14679
14680
14681     /**
14682      * Resizes the dialog to fit the specified content size.
14683      * @param {Number} width
14684      * @param {Number} height
14685      * @return {Roo.BasicDialog} this
14686      */
14687     setContentSize : function(w, h){
14688         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14689         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14690         //if(!this.el.isBorderBox()){
14691             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14692             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14693         //}
14694         if(this.tabs){
14695             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14696             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14697         }
14698         this.resizeTo(w, h);
14699         return this;
14700     },
14701
14702     /**
14703      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
14704      * executed in response to a particular key being pressed while the dialog is active.
14705      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14706      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14707      * @param {Function} fn The function to call
14708      * @param {Object} scope (optional) The scope of the function
14709      * @return {Roo.BasicDialog} this
14710      */
14711     addKeyListener : function(key, fn, scope){
14712         var keyCode, shift, ctrl, alt;
14713         if(typeof key == "object" && !(key instanceof Array)){
14714             keyCode = key["key"];
14715             shift = key["shift"];
14716             ctrl = key["ctrl"];
14717             alt = key["alt"];
14718         }else{
14719             keyCode = key;
14720         }
14721         var handler = function(dlg, e){
14722             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14723                 var k = e.getKey();
14724                 if(keyCode instanceof Array){
14725                     for(var i = 0, len = keyCode.length; i < len; i++){
14726                         if(keyCode[i] == k){
14727                           fn.call(scope || window, dlg, k, e);
14728                           return;
14729                         }
14730                     }
14731                 }else{
14732                     if(k == keyCode){
14733                         fn.call(scope || window, dlg, k, e);
14734                     }
14735                 }
14736             }
14737         };
14738         this.on("keydown", handler);
14739         return this;
14740     },
14741
14742     /**
14743      * Returns the TabPanel component (creates it if it doesn't exist).
14744      * Note: If you wish to simply check for the existence of tabs without creating them,
14745      * check for a null 'tabs' property.
14746      * @return {Roo.TabPanel} The tabs component
14747      */
14748     getTabs : function(){
14749         if(!this.tabs){
14750             this.el.addClass("x-dlg-auto-tabs");
14751             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14752             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14753         }
14754         return this.tabs;
14755     },
14756
14757     /**
14758      * Adds a button to the footer section of the dialog.
14759      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14760      * object or a valid Roo.DomHelper element config
14761      * @param {Function} handler The function called when the button is clicked
14762      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14763      * @return {Roo.Button} The new button
14764      */
14765     addButton : function(config, handler, scope){
14766         var dh = Roo.DomHelper;
14767         if(!this.footer){
14768             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14769         }
14770         if(!this.btnContainer){
14771             var tb = this.footer.createChild({
14772
14773                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14774                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14775             }, null, true);
14776             this.btnContainer = tb.firstChild.firstChild.firstChild;
14777         }
14778         var bconfig = {
14779             handler: handler,
14780             scope: scope,
14781             minWidth: this.minButtonWidth,
14782             hideParent:true
14783         };
14784         if(typeof config == "string"){
14785             bconfig.text = config;
14786         }else{
14787             if(config.tag){
14788                 bconfig.dhconfig = config;
14789             }else{
14790                 Roo.apply(bconfig, config);
14791             }
14792         }
14793         var fc = false;
14794         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14795             bconfig.position = Math.max(0, bconfig.position);
14796             fc = this.btnContainer.childNodes[bconfig.position];
14797         }
14798          
14799         var btn = new Roo.Button(
14800             fc ? 
14801                 this.btnContainer.insertBefore(document.createElement("td"),fc)
14802                 : this.btnContainer.appendChild(document.createElement("td")),
14803             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
14804             bconfig
14805         );
14806         this.syncBodyHeight();
14807         if(!this.buttons){
14808             /**
14809              * Array of all the buttons that have been added to this dialog via addButton
14810              * @type Array
14811              */
14812             this.buttons = [];
14813         }
14814         this.buttons.push(btn);
14815         return btn;
14816     },
14817
14818     /**
14819      * Sets the default button to be focused when the dialog is displayed.
14820      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14821      * @return {Roo.BasicDialog} this
14822      */
14823     setDefaultButton : function(btn){
14824         this.defaultButton = btn;
14825         return this;
14826     },
14827
14828     // private
14829     getHeaderFooterHeight : function(safe){
14830         var height = 0;
14831         if(this.header){
14832            height += this.header.getHeight();
14833         }
14834         if(this.footer){
14835            var fm = this.footer.getMargins();
14836             height += (this.footer.getHeight()+fm.top+fm.bottom);
14837         }
14838         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14839         height += this.centerBg.getPadding("tb");
14840         return height;
14841     },
14842
14843     // private
14844     syncBodyHeight : function(){
14845         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
14846         var height = this.size.height - this.getHeaderFooterHeight(false);
14847         bd.setHeight(height-bd.getMargins("tb"));
14848         var hh = this.header.getHeight();
14849         var h = this.size.height-hh;
14850         cb.setHeight(h);
14851         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14852         bw.setHeight(h-cb.getPadding("tb"));
14853         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14854         bd.setWidth(bw.getWidth(true));
14855         if(this.tabs){
14856             this.tabs.syncHeight();
14857             if(Roo.isIE){
14858                 this.tabs.el.repaint();
14859             }
14860         }
14861     },
14862
14863     /**
14864      * Restores the previous state of the dialog if Roo.state is configured.
14865      * @return {Roo.BasicDialog} this
14866      */
14867     restoreState : function(){
14868         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14869         if(box && box.width){
14870             this.xy = [box.x, box.y];
14871             this.resizeTo(box.width, box.height);
14872         }
14873         return this;
14874     },
14875
14876     // private
14877     beforeShow : function(){
14878         this.expand();
14879         if(this.fixedcenter){
14880             this.xy = this.el.getCenterXY(true);
14881         }
14882         if(this.modal){
14883             Roo.get(document.body).addClass("x-body-masked");
14884             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14885             this.mask.show();
14886         }
14887         this.constrainXY();
14888     },
14889
14890     // private
14891     animShow : function(){
14892         var b = Roo.get(this.animateTarget).getBox();
14893         this.proxy.setSize(b.width, b.height);
14894         this.proxy.setLocation(b.x, b.y);
14895         this.proxy.show();
14896         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
14897                     true, .35, this.showEl.createDelegate(this));
14898     },
14899
14900     /**
14901      * Shows the dialog.
14902      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
14903      * @return {Roo.BasicDialog} this
14904      */
14905     show : function(animateTarget){
14906         if (this.fireEvent("beforeshow", this) === false){
14907             return;
14908         }
14909         if(this.syncHeightBeforeShow){
14910             this.syncBodyHeight();
14911         }else if(this.firstShow){
14912             this.firstShow = false;
14913             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
14914         }
14915         this.animateTarget = animateTarget || this.animateTarget;
14916         if(!this.el.isVisible()){
14917             this.beforeShow();
14918             if(this.animateTarget && Roo.get(this.animateTarget)){
14919                 this.animShow();
14920             }else{
14921                 this.showEl();
14922             }
14923         }
14924         return this;
14925     },
14926
14927     // private
14928     showEl : function(){
14929         this.proxy.hide();
14930         this.el.setXY(this.xy);
14931         this.el.show();
14932         this.adjustAssets(true);
14933         this.toFront();
14934         this.focus();
14935         // IE peekaboo bug - fix found by Dave Fenwick
14936         if(Roo.isIE){
14937             this.el.repaint();
14938         }
14939         this.fireEvent("show", this);
14940     },
14941
14942     /**
14943      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
14944      * dialog itself will receive focus.
14945      */
14946     focus : function(){
14947         if(this.defaultButton){
14948             this.defaultButton.focus();
14949         }else{
14950             this.focusEl.focus();
14951         }
14952     },
14953
14954     // private
14955     constrainXY : function(){
14956         if(this.constraintoviewport !== false){
14957             if(!this.viewSize){
14958                 if(this.container){
14959                     var s = this.container.getSize();
14960                     this.viewSize = [s.width, s.height];
14961                 }else{
14962                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
14963                 }
14964             }
14965             var s = Roo.get(this.container||document).getScroll();
14966
14967             var x = this.xy[0], y = this.xy[1];
14968             var w = this.size.width, h = this.size.height;
14969             var vw = this.viewSize[0], vh = this.viewSize[1];
14970             // only move it if it needs it
14971             var moved = false;
14972             // first validate right/bottom
14973             if(x + w > vw+s.left){
14974                 x = vw - w;
14975                 moved = true;
14976             }
14977             if(y + h > vh+s.top){
14978                 y = vh - h;
14979                 moved = true;
14980             }
14981             // then make sure top/left isn't negative
14982             if(x < s.left){
14983                 x = s.left;
14984                 moved = true;
14985             }
14986             if(y < s.top){
14987                 y = s.top;
14988                 moved = true;
14989             }
14990             if(moved){
14991                 // cache xy
14992                 this.xy = [x, y];
14993                 if(this.isVisible()){
14994                     this.el.setLocation(x, y);
14995                     this.adjustAssets();
14996                 }
14997             }
14998         }
14999     },
15000
15001     // private
15002     onDrag : function(){
15003         if(!this.proxyDrag){
15004             this.xy = this.el.getXY();
15005             this.adjustAssets();
15006         }
15007     },
15008
15009     // private
15010     adjustAssets : function(doShow){
15011         var x = this.xy[0], y = this.xy[1];
15012         var w = this.size.width, h = this.size.height;
15013         if(doShow === true){
15014             if(this.shadow){
15015                 this.shadow.show(this.el);
15016             }
15017             if(this.shim){
15018                 this.shim.show();
15019             }
15020         }
15021         if(this.shadow && this.shadow.isVisible()){
15022             this.shadow.show(this.el);
15023         }
15024         if(this.shim && this.shim.isVisible()){
15025             this.shim.setBounds(x, y, w, h);
15026         }
15027     },
15028
15029     // private
15030     adjustViewport : function(w, h){
15031         if(!w || !h){
15032             w = Roo.lib.Dom.getViewWidth();
15033             h = Roo.lib.Dom.getViewHeight();
15034         }
15035         // cache the size
15036         this.viewSize = [w, h];
15037         if(this.modal && this.mask.isVisible()){
15038             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
15039             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15040         }
15041         if(this.isVisible()){
15042             this.constrainXY();
15043         }
15044     },
15045
15046     /**
15047      * Destroys this dialog and all its supporting elements (including any tabs, shim,
15048      * shadow, proxy, mask, etc.)  Also removes all event listeners.
15049      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
15050      */
15051     destroy : function(removeEl){
15052         if(this.isVisible()){
15053             this.animateTarget = null;
15054             this.hide();
15055         }
15056         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
15057         if(this.tabs){
15058             this.tabs.destroy(removeEl);
15059         }
15060         Roo.destroy(
15061              this.shim,
15062              this.proxy,
15063              this.resizer,
15064              this.close,
15065              this.mask
15066         );
15067         if(this.dd){
15068             this.dd.unreg();
15069         }
15070         if(this.buttons){
15071            for(var i = 0, len = this.buttons.length; i < len; i++){
15072                this.buttons[i].destroy();
15073            }
15074         }
15075         this.el.removeAllListeners();
15076         if(removeEl === true){
15077             this.el.update("");
15078             this.el.remove();
15079         }
15080         Roo.DialogManager.unregister(this);
15081     },
15082
15083     // private
15084     startMove : function(){
15085         if(this.proxyDrag){
15086             this.proxy.show();
15087         }
15088         if(this.constraintoviewport !== false){
15089             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
15090         }
15091     },
15092
15093     // private
15094     endMove : function(){
15095         if(!this.proxyDrag){
15096             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
15097         }else{
15098             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
15099             this.proxy.hide();
15100         }
15101         this.refreshSize();
15102         this.adjustAssets();
15103         this.focus();
15104         this.fireEvent("move", this, this.xy[0], this.xy[1]);
15105     },
15106
15107     /**
15108      * Brings this dialog to the front of any other visible dialogs
15109      * @return {Roo.BasicDialog} this
15110      */
15111     toFront : function(){
15112         Roo.DialogManager.bringToFront(this);
15113         return this;
15114     },
15115
15116     /**
15117      * Sends this dialog to the back (under) of any other visible dialogs
15118      * @return {Roo.BasicDialog} this
15119      */
15120     toBack : function(){
15121         Roo.DialogManager.sendToBack(this);
15122         return this;
15123     },
15124
15125     /**
15126      * Centers this dialog in the viewport
15127      * @return {Roo.BasicDialog} this
15128      */
15129     center : function(){
15130         var xy = this.el.getCenterXY(true);
15131         this.moveTo(xy[0], xy[1]);
15132         return this;
15133     },
15134
15135     /**
15136      * Moves the dialog's top-left corner to the specified point
15137      * @param {Number} x
15138      * @param {Number} y
15139      * @return {Roo.BasicDialog} this
15140      */
15141     moveTo : function(x, y){
15142         this.xy = [x,y];
15143         if(this.isVisible()){
15144             this.el.setXY(this.xy);
15145             this.adjustAssets();
15146         }
15147         return this;
15148     },
15149
15150     /**
15151      * Aligns the dialog to the specified element
15152      * @param {String/HTMLElement/Roo.Element} element The element to align to.
15153      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
15154      * @param {Array} offsets (optional) Offset the positioning by [x, y]
15155      * @return {Roo.BasicDialog} this
15156      */
15157     alignTo : function(element, position, offsets){
15158         this.xy = this.el.getAlignToXY(element, position, offsets);
15159         if(this.isVisible()){
15160             this.el.setXY(this.xy);
15161             this.adjustAssets();
15162         }
15163         return this;
15164     },
15165
15166     /**
15167      * Anchors an element to another element and realigns it when the window is resized.
15168      * @param {String/HTMLElement/Roo.Element} element The element to align to.
15169      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
15170      * @param {Array} offsets (optional) Offset the positioning by [x, y]
15171      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
15172      * is a number, it is used as the buffer delay (defaults to 50ms).
15173      * @return {Roo.BasicDialog} this
15174      */
15175     anchorTo : function(el, alignment, offsets, monitorScroll){
15176         var action = function(){
15177             this.alignTo(el, alignment, offsets);
15178         };
15179         Roo.EventManager.onWindowResize(action, this);
15180         var tm = typeof monitorScroll;
15181         if(tm != 'undefined'){
15182             Roo.EventManager.on(window, 'scroll', action, this,
15183                 {buffer: tm == 'number' ? monitorScroll : 50});
15184         }
15185         action.call(this);
15186         return this;
15187     },
15188
15189     /**
15190      * Returns true if the dialog is visible
15191      * @return {Boolean}
15192      */
15193     isVisible : function(){
15194         return this.el.isVisible();
15195     },
15196
15197     // private
15198     animHide : function(callback){
15199         var b = Roo.get(this.animateTarget).getBox();
15200         this.proxy.show();
15201         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
15202         this.el.hide();
15203         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
15204                     this.hideEl.createDelegate(this, [callback]));
15205     },
15206
15207     /**
15208      * Hides the dialog.
15209      * @param {Function} callback (optional) Function to call when the dialog is hidden
15210      * @return {Roo.BasicDialog} this
15211      */
15212     hide : function(callback){
15213         if (this.fireEvent("beforehide", this) === false){
15214             return;
15215         }
15216         if(this.shadow){
15217             this.shadow.hide();
15218         }
15219         if(this.shim) {
15220           this.shim.hide();
15221         }
15222         // sometimes animateTarget seems to get set.. causing problems...
15223         // this just double checks..
15224         if(this.animateTarget && Roo.get(this.animateTarget)) {
15225            this.animHide(callback);
15226         }else{
15227             this.el.hide();
15228             this.hideEl(callback);
15229         }
15230         return this;
15231     },
15232
15233     // private
15234     hideEl : function(callback){
15235         this.proxy.hide();
15236         if(this.modal){
15237             this.mask.hide();
15238             Roo.get(document.body).removeClass("x-body-masked");
15239         }
15240         this.fireEvent("hide", this);
15241         if(typeof callback == "function"){
15242             callback();
15243         }
15244     },
15245
15246     // private
15247     hideAction : function(){
15248         this.setLeft("-10000px");
15249         this.setTop("-10000px");
15250         this.setStyle("visibility", "hidden");
15251     },
15252
15253     // private
15254     refreshSize : function(){
15255         this.size = this.el.getSize();
15256         this.xy = this.el.getXY();
15257         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
15258     },
15259
15260     // private
15261     // z-index is managed by the DialogManager and may be overwritten at any time
15262     setZIndex : function(index){
15263         if(this.modal){
15264             this.mask.setStyle("z-index", index);
15265         }
15266         if(this.shim){
15267             this.shim.setStyle("z-index", ++index);
15268         }
15269         if(this.shadow){
15270             this.shadow.setZIndex(++index);
15271         }
15272         this.el.setStyle("z-index", ++index);
15273         if(this.proxy){
15274             this.proxy.setStyle("z-index", ++index);
15275         }
15276         if(this.resizer){
15277             this.resizer.proxy.setStyle("z-index", ++index);
15278         }
15279
15280         this.lastZIndex = index;
15281     },
15282
15283     /**
15284      * Returns the element for this dialog
15285      * @return {Roo.Element} The underlying dialog Element
15286      */
15287     getEl : function(){
15288         return this.el;
15289     }
15290 });
15291
15292 /**
15293  * @class Roo.DialogManager
15294  * Provides global access to BasicDialogs that have been created and
15295  * support for z-indexing (layering) multiple open dialogs.
15296  */
15297 Roo.DialogManager = function(){
15298     var list = {};
15299     var accessList = [];
15300     var front = null;
15301
15302     // private
15303     var sortDialogs = function(d1, d2){
15304         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
15305     };
15306
15307     // private
15308     var orderDialogs = function(){
15309         accessList.sort(sortDialogs);
15310         var seed = Roo.DialogManager.zseed;
15311         for(var i = 0, len = accessList.length; i < len; i++){
15312             var dlg = accessList[i];
15313             if(dlg){
15314                 dlg.setZIndex(seed + (i*10));
15315             }
15316         }
15317     };
15318
15319     return {
15320         /**
15321          * The starting z-index for BasicDialogs (defaults to 9000)
15322          * @type Number The z-index value
15323          */
15324         zseed : 9000,
15325
15326         // private
15327         register : function(dlg){
15328             list[dlg.id] = dlg;
15329             accessList.push(dlg);
15330         },
15331
15332         // private
15333         unregister : function(dlg){
15334             delete list[dlg.id];
15335             var i=0;
15336             var len=0;
15337             if(!accessList.indexOf){
15338                 for(  i = 0, len = accessList.length; i < len; i++){
15339                     if(accessList[i] == dlg){
15340                         accessList.splice(i, 1);
15341                         return;
15342                     }
15343                 }
15344             }else{
15345                  i = accessList.indexOf(dlg);
15346                 if(i != -1){
15347                     accessList.splice(i, 1);
15348                 }
15349             }
15350         },
15351
15352         /**
15353          * Gets a registered dialog by id
15354          * @param {String/Object} id The id of the dialog or a dialog
15355          * @return {Roo.BasicDialog} this
15356          */
15357         get : function(id){
15358             return typeof id == "object" ? id : list[id];
15359         },
15360
15361         /**
15362          * Brings the specified dialog to the front
15363          * @param {String/Object} dlg The id of the dialog or a dialog
15364          * @return {Roo.BasicDialog} this
15365          */
15366         bringToFront : function(dlg){
15367             dlg = this.get(dlg);
15368             if(dlg != front){
15369                 front = dlg;
15370                 dlg._lastAccess = new Date().getTime();
15371                 orderDialogs();
15372             }
15373             return dlg;
15374         },
15375
15376         /**
15377          * Sends the specified dialog to the back
15378          * @param {String/Object} dlg The id of the dialog or a dialog
15379          * @return {Roo.BasicDialog} this
15380          */
15381         sendToBack : function(dlg){
15382             dlg = this.get(dlg);
15383             dlg._lastAccess = -(new Date().getTime());
15384             orderDialogs();
15385             return dlg;
15386         },
15387
15388         /**
15389          * Hides all dialogs
15390          */
15391         hideAll : function(){
15392             for(var id in list){
15393                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
15394                     list[id].hide();
15395                 }
15396             }
15397         }
15398     };
15399 }();
15400
15401 /**
15402  * @class Roo.LayoutDialog
15403  * @extends Roo.BasicDialog
15404  * Dialog which provides adjustments for working with a layout in a Dialog.
15405  * Add your necessary layout config options to the dialog's config.<br>
15406  * Example usage (including a nested layout):
15407  * <pre><code>
15408 if(!dialog){
15409     dialog = new Roo.LayoutDialog("download-dlg", {
15410         modal: true,
15411         width:600,
15412         height:450,
15413         shadow:true,
15414         minWidth:500,
15415         minHeight:350,
15416         autoTabs:true,
15417         proxyDrag:true,
15418         // layout config merges with the dialog config
15419         center:{
15420             tabPosition: "top",
15421             alwaysShowTabs: true
15422         }
15423     });
15424     dialog.addKeyListener(27, dialog.hide, dialog);
15425     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
15426     dialog.addButton("Build It!", this.getDownload, this);
15427
15428     // we can even add nested layouts
15429     var innerLayout = new Roo.BorderLayout("dl-inner", {
15430         east: {
15431             initialSize: 200,
15432             autoScroll:true,
15433             split:true
15434         },
15435         center: {
15436             autoScroll:true
15437         }
15438     });
15439     innerLayout.beginUpdate();
15440     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
15441     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
15442     innerLayout.endUpdate(true);
15443
15444     var layout = dialog.getLayout();
15445     layout.beginUpdate();
15446     layout.add("center", new Roo.ContentPanel("standard-panel",
15447                         {title: "Download the Source", fitToFrame:true}));
15448     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
15449                {title: "Build your own roo.js"}));
15450     layout.getRegion("center").showPanel(sp);
15451     layout.endUpdate();
15452 }
15453 </code></pre>
15454     * @constructor
15455     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
15456     * @param {Object} config configuration options
15457   */
15458 Roo.LayoutDialog = function(el, cfg){
15459     
15460     var config=  cfg;
15461     if (typeof(cfg) == 'undefined') {
15462         config = Roo.apply({}, el);
15463         // not sure why we use documentElement here.. - it should always be body.
15464         // IE7 borks horribly if we use documentElement.
15465         // webkit also does not like documentElement - it creates a body element...
15466         el = Roo.get( document.body || document.documentElement ).createChild();
15467         //config.autoCreate = true;
15468     }
15469     
15470     
15471     config.autoTabs = false;
15472     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
15473     this.body.setStyle({overflow:"hidden", position:"relative"});
15474     this.layout = new Roo.BorderLayout(this.body.dom, config);
15475     this.layout.monitorWindowResize = false;
15476     this.el.addClass("x-dlg-auto-layout");
15477     // fix case when center region overwrites center function
15478     this.center = Roo.BasicDialog.prototype.center;
15479     this.on("show", this.layout.layout, this.layout, true);
15480     if (config.items) {
15481         var xitems = config.items;
15482         delete config.items;
15483         Roo.each(xitems, this.addxtype, this);
15484     }
15485     
15486     
15487 };
15488 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
15489     /**
15490      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
15491      * @deprecated
15492      */
15493     endUpdate : function(){
15494         this.layout.endUpdate();
15495     },
15496
15497     /**
15498      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
15499      *  @deprecated
15500      */
15501     beginUpdate : function(){
15502         this.layout.beginUpdate();
15503     },
15504
15505     /**
15506      * Get the BorderLayout for this dialog
15507      * @return {Roo.BorderLayout}
15508      */
15509     getLayout : function(){
15510         return this.layout;
15511     },
15512
15513     showEl : function(){
15514         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
15515         if(Roo.isIE7){
15516             this.layout.layout();
15517         }
15518     },
15519
15520     // private
15521     // Use the syncHeightBeforeShow config option to control this automatically
15522     syncBodyHeight : function(){
15523         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
15524         if(this.layout){this.layout.layout();}
15525     },
15526     
15527       /**
15528      * Add an xtype element (actually adds to the layout.)
15529      * @return {Object} xdata xtype object data.
15530      */
15531     
15532     addxtype : function(c) {
15533         return this.layout.addxtype(c);
15534     }
15535 });/*
15536  * Based on:
15537  * Ext JS Library 1.1.1
15538  * Copyright(c) 2006-2007, Ext JS, LLC.
15539  *
15540  * Originally Released Under LGPL - original licence link has changed is not relivant.
15541  *
15542  * Fork - LGPL
15543  * <script type="text/javascript">
15544  */
15545  
15546 /**
15547  * @class Roo.MessageBox
15548  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
15549  * Example usage:
15550  *<pre><code>
15551 // Basic alert:
15552 Roo.Msg.alert('Status', 'Changes saved successfully.');
15553
15554 // Prompt for user data:
15555 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
15556     if (btn == 'ok'){
15557         // process text value...
15558     }
15559 });
15560
15561 // Show a dialog using config options:
15562 Roo.Msg.show({
15563    title:'Save Changes?',
15564    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
15565    buttons: Roo.Msg.YESNOCANCEL,
15566    fn: processResult,
15567    animEl: 'elId'
15568 });
15569 </code></pre>
15570  * @singleton
15571  */
15572 Roo.MessageBox = function(){
15573     var dlg, opt, mask, waitTimer;
15574     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
15575     var buttons, activeTextEl, bwidth;
15576
15577     // private
15578     var handleButton = function(button){
15579         dlg.hide();
15580         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
15581     };
15582
15583     // private
15584     var handleHide = function(){
15585         if(opt && opt.cls){
15586             dlg.el.removeClass(opt.cls);
15587         }
15588         if(waitTimer){
15589             Roo.TaskMgr.stop(waitTimer);
15590             waitTimer = null;
15591         }
15592     };
15593
15594     // private
15595     var updateButtons = function(b){
15596         var width = 0;
15597         if(!b){
15598             buttons["ok"].hide();
15599             buttons["cancel"].hide();
15600             buttons["yes"].hide();
15601             buttons["no"].hide();
15602             dlg.footer.dom.style.display = 'none';
15603             return width;
15604         }
15605         dlg.footer.dom.style.display = '';
15606         for(var k in buttons){
15607             if(typeof buttons[k] != "function"){
15608                 if(b[k]){
15609                     buttons[k].show();
15610                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15611                     width += buttons[k].el.getWidth()+15;
15612                 }else{
15613                     buttons[k].hide();
15614                 }
15615             }
15616         }
15617         return width;
15618     };
15619
15620     // private
15621     var handleEsc = function(d, k, e){
15622         if(opt && opt.closable !== false){
15623             dlg.hide();
15624         }
15625         if(e){
15626             e.stopEvent();
15627         }
15628     };
15629
15630     return {
15631         /**
15632          * Returns a reference to the underlying {@link Roo.BasicDialog} element
15633          * @return {Roo.BasicDialog} The BasicDialog element
15634          */
15635         getDialog : function(){
15636            if(!dlg){
15637                 dlg = new Roo.BasicDialog("x-msg-box", {
15638                     autoCreate : true,
15639                     shadow: true,
15640                     draggable: true,
15641                     resizable:false,
15642                     constraintoviewport:false,
15643                     fixedcenter:true,
15644                     collapsible : false,
15645                     shim:true,
15646                     modal: true,
15647                     width:400, height:100,
15648                     buttonAlign:"center",
15649                     closeClick : function(){
15650                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15651                             handleButton("no");
15652                         }else{
15653                             handleButton("cancel");
15654                         }
15655                     }
15656                 });
15657                 dlg.on("hide", handleHide);
15658                 mask = dlg.mask;
15659                 dlg.addKeyListener(27, handleEsc);
15660                 buttons = {};
15661                 var bt = this.buttonText;
15662                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15663                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15664                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15665                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15666                 bodyEl = dlg.body.createChild({
15667
15668                     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>'
15669                 });
15670                 msgEl = bodyEl.dom.firstChild;
15671                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15672                 textboxEl.enableDisplayMode();
15673                 textboxEl.addKeyListener([10,13], function(){
15674                     if(dlg.isVisible() && opt && opt.buttons){
15675                         if(opt.buttons.ok){
15676                             handleButton("ok");
15677                         }else if(opt.buttons.yes){
15678                             handleButton("yes");
15679                         }
15680                     }
15681                 });
15682                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15683                 textareaEl.enableDisplayMode();
15684                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15685                 progressEl.enableDisplayMode();
15686                 var pf = progressEl.dom.firstChild;
15687                 if (pf) {
15688                     pp = Roo.get(pf.firstChild);
15689                     pp.setHeight(pf.offsetHeight);
15690                 }
15691                 
15692             }
15693             return dlg;
15694         },
15695
15696         /**
15697          * Updates the message box body text
15698          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15699          * the XHTML-compliant non-breaking space character '&amp;#160;')
15700          * @return {Roo.MessageBox} This message box
15701          */
15702         updateText : function(text){
15703             if(!dlg.isVisible() && !opt.width){
15704                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15705             }
15706             msgEl.innerHTML = text || '&#160;';
15707       
15708             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
15709             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
15710             var w = Math.max(
15711                     Math.min(opt.width || cw , this.maxWidth), 
15712                     Math.max(opt.minWidth || this.minWidth, bwidth)
15713             );
15714             if(opt.prompt){
15715                 activeTextEl.setWidth(w);
15716             }
15717             if(dlg.isVisible()){
15718                 dlg.fixedcenter = false;
15719             }
15720             // to big, make it scroll. = But as usual stupid IE does not support
15721             // !important..
15722             
15723             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
15724                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
15725                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
15726             } else {
15727                 bodyEl.dom.style.height = '';
15728                 bodyEl.dom.style.overflowY = '';
15729             }
15730             if (cw > w) {
15731                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
15732             } else {
15733                 bodyEl.dom.style.overflowX = '';
15734             }
15735             
15736             dlg.setContentSize(w, bodyEl.getHeight());
15737             if(dlg.isVisible()){
15738                 dlg.fixedcenter = true;
15739             }
15740             return this;
15741         },
15742
15743         /**
15744          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
15745          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15746          * @param {Number} value Any number between 0 and 1 (e.g., .5)
15747          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15748          * @return {Roo.MessageBox} This message box
15749          */
15750         updateProgress : function(value, text){
15751             if(text){
15752                 this.updateText(text);
15753             }
15754             if (pp) { // weird bug on my firefox - for some reason this is not defined
15755                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15756             }
15757             return this;
15758         },        
15759
15760         /**
15761          * Returns true if the message box is currently displayed
15762          * @return {Boolean} True if the message box is visible, else false
15763          */
15764         isVisible : function(){
15765             return dlg && dlg.isVisible();  
15766         },
15767
15768         /**
15769          * Hides the message box if it is displayed
15770          */
15771         hide : function(){
15772             if(this.isVisible()){
15773                 dlg.hide();
15774             }  
15775         },
15776
15777         /**
15778          * Displays a new message box, or reinitializes an existing message box, based on the config options
15779          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15780          * The following config object properties are supported:
15781          * <pre>
15782 Property    Type             Description
15783 ----------  ---------------  ------------------------------------------------------------------------------------
15784 animEl            String/Element   An id or Element from which the message box should animate as it opens and
15785                                    closes (defaults to undefined)
15786 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15787                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
15788 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
15789                                    progress and wait dialogs will ignore this property and always hide the
15790                                    close button as they can only be closed programmatically.
15791 cls               String           A custom CSS class to apply to the message box element
15792 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
15793                                    displayed (defaults to 75)
15794 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
15795                                    function will be btn (the name of the button that was clicked, if applicable,
15796                                    e.g. "ok"), and text (the value of the active text field, if applicable).
15797                                    Progress and wait dialogs will ignore this option since they do not respond to
15798                                    user actions and can only be closed programmatically, so any required function
15799                                    should be called by the same code after it closes the dialog.
15800 icon              String           A CSS class that provides a background image to be used as an icon for
15801                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15802 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
15803 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
15804 modal             Boolean          False to allow user interaction with the page while the message box is
15805                                    displayed (defaults to true)
15806 msg               String           A string that will replace the existing message box body text (defaults
15807                                    to the XHTML-compliant non-breaking space character '&#160;')
15808 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
15809 progress          Boolean          True to display a progress bar (defaults to false)
15810 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
15811 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
15812 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
15813 title             String           The title text
15814 value             String           The string value to set into the active textbox element if displayed
15815 wait              Boolean          True to display a progress bar (defaults to false)
15816 width             Number           The width of the dialog in pixels
15817 </pre>
15818          *
15819          * Example usage:
15820          * <pre><code>
15821 Roo.Msg.show({
15822    title: 'Address',
15823    msg: 'Please enter your address:',
15824    width: 300,
15825    buttons: Roo.MessageBox.OKCANCEL,
15826    multiline: true,
15827    fn: saveAddress,
15828    animEl: 'addAddressBtn'
15829 });
15830 </code></pre>
15831          * @param {Object} config Configuration options
15832          * @return {Roo.MessageBox} This message box
15833          */
15834         show : function(options)
15835         {
15836             
15837             // this causes nightmares if you show one dialog after another
15838             // especially on callbacks..
15839              
15840             if(this.isVisible()){
15841                 
15842                 this.hide();
15843                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
15844                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
15845                 Roo.log("New Dialog Message:" +  options.msg )
15846                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
15847                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
15848                 
15849             }
15850             var d = this.getDialog();
15851             opt = options;
15852             d.setTitle(opt.title || "&#160;");
15853             d.close.setDisplayed(opt.closable !== false);
15854             activeTextEl = textboxEl;
15855             opt.prompt = opt.prompt || (opt.multiline ? true : false);
15856             if(opt.prompt){
15857                 if(opt.multiline){
15858                     textboxEl.hide();
15859                     textareaEl.show();
15860                     textareaEl.setHeight(typeof opt.multiline == "number" ?
15861                         opt.multiline : this.defaultTextHeight);
15862                     activeTextEl = textareaEl;
15863                 }else{
15864                     textboxEl.show();
15865                     textareaEl.hide();
15866                 }
15867             }else{
15868                 textboxEl.hide();
15869                 textareaEl.hide();
15870             }
15871             progressEl.setDisplayed(opt.progress === true);
15872             this.updateProgress(0);
15873             activeTextEl.dom.value = opt.value || "";
15874             if(opt.prompt){
15875                 dlg.setDefaultButton(activeTextEl);
15876             }else{
15877                 var bs = opt.buttons;
15878                 var db = null;
15879                 if(bs && bs.ok){
15880                     db = buttons["ok"];
15881                 }else if(bs && bs.yes){
15882                     db = buttons["yes"];
15883                 }
15884                 dlg.setDefaultButton(db);
15885             }
15886             bwidth = updateButtons(opt.buttons);
15887             this.updateText(opt.msg);
15888             if(opt.cls){
15889                 d.el.addClass(opt.cls);
15890             }
15891             d.proxyDrag = opt.proxyDrag === true;
15892             d.modal = opt.modal !== false;
15893             d.mask = opt.modal !== false ? mask : false;
15894             if(!d.isVisible()){
15895                 // force it to the end of the z-index stack so it gets a cursor in FF
15896                 document.body.appendChild(dlg.el.dom);
15897                 d.animateTarget = null;
15898                 d.show(options.animEl);
15899             }
15900             return this;
15901         },
15902
15903         /**
15904          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
15905          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
15906          * and closing the message box when the process is complete.
15907          * @param {String} title The title bar text
15908          * @param {String} msg The message box body text
15909          * @return {Roo.MessageBox} This message box
15910          */
15911         progress : function(title, msg){
15912             this.show({
15913                 title : title,
15914                 msg : msg,
15915                 buttons: false,
15916                 progress:true,
15917                 closable:false,
15918                 minWidth: this.minProgressWidth,
15919                 modal : true
15920             });
15921             return this;
15922         },
15923
15924         /**
15925          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
15926          * If a callback function is passed it will be called after the user clicks the button, and the
15927          * id of the button that was clicked will be passed as the only parameter to the callback
15928          * (could also be the top-right close button).
15929          * @param {String} title The title bar text
15930          * @param {String} msg The message box body text
15931          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15932          * @param {Object} scope (optional) The scope of the callback function
15933          * @return {Roo.MessageBox} This message box
15934          */
15935         alert : function(title, msg, fn, scope){
15936             this.show({
15937                 title : title,
15938                 msg : msg,
15939                 buttons: this.OK,
15940                 fn: fn,
15941                 scope : scope,
15942                 modal : true
15943             });
15944             return this;
15945         },
15946
15947         /**
15948          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
15949          * interaction while waiting for a long-running process to complete that does not have defined intervals.
15950          * You are responsible for closing the message box when the process is complete.
15951          * @param {String} msg The message box body text
15952          * @param {String} title (optional) The title bar text
15953          * @return {Roo.MessageBox} This message box
15954          */
15955         wait : function(msg, title){
15956             this.show({
15957                 title : title,
15958                 msg : msg,
15959                 buttons: false,
15960                 closable:false,
15961                 progress:true,
15962                 modal:true,
15963                 width:300,
15964                 wait:true
15965             });
15966             waitTimer = Roo.TaskMgr.start({
15967                 run: function(i){
15968                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
15969                 },
15970                 interval: 1000
15971             });
15972             return this;
15973         },
15974
15975         /**
15976          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
15977          * If a callback function is passed it will be called after the user clicks either button, and the id of the
15978          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
15979          * @param {String} title The title bar text
15980          * @param {String} msg The message box body text
15981          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15982          * @param {Object} scope (optional) The scope of the callback function
15983          * @return {Roo.MessageBox} This message box
15984          */
15985         confirm : function(title, msg, fn, scope){
15986             this.show({
15987                 title : title,
15988                 msg : msg,
15989                 buttons: this.YESNO,
15990                 fn: fn,
15991                 scope : scope,
15992                 modal : true
15993             });
15994             return this;
15995         },
15996
15997         /**
15998          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
15999          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
16000          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
16001          * (could also be the top-right close button) and the text that was entered will be passed as the two
16002          * parameters to the callback.
16003          * @param {String} title The title bar text
16004          * @param {String} msg The message box body text
16005          * @param {Function} fn (optional) The callback function invoked after the message box is closed
16006          * @param {Object} scope (optional) The scope of the callback function
16007          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
16008          * property, or the height in pixels to create the textbox (defaults to false / single-line)
16009          * @return {Roo.MessageBox} This message box
16010          */
16011         prompt : function(title, msg, fn, scope, multiline){
16012             this.show({
16013                 title : title,
16014                 msg : msg,
16015                 buttons: this.OKCANCEL,
16016                 fn: fn,
16017                 minWidth:250,
16018                 scope : scope,
16019                 prompt:true,
16020                 multiline: multiline,
16021                 modal : true
16022             });
16023             return this;
16024         },
16025
16026         /**
16027          * Button config that displays a single OK button
16028          * @type Object
16029          */
16030         OK : {ok:true},
16031         /**
16032          * Button config that displays Yes and No buttons
16033          * @type Object
16034          */
16035         YESNO : {yes:true, no:true},
16036         /**
16037          * Button config that displays OK and Cancel buttons
16038          * @type Object
16039          */
16040         OKCANCEL : {ok:true, cancel:true},
16041         /**
16042          * Button config that displays Yes, No and Cancel buttons
16043          * @type Object
16044          */
16045         YESNOCANCEL : {yes:true, no:true, cancel:true},
16046
16047         /**
16048          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
16049          * @type Number
16050          */
16051         defaultTextHeight : 75,
16052         /**
16053          * The maximum width in pixels of the message box (defaults to 600)
16054          * @type Number
16055          */
16056         maxWidth : 600,
16057         /**
16058          * The minimum width in pixels of the message box (defaults to 100)
16059          * @type Number
16060          */
16061         minWidth : 100,
16062         /**
16063          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
16064          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
16065          * @type Number
16066          */
16067         minProgressWidth : 250,
16068         /**
16069          * An object containing the default button text strings that can be overriden for localized language support.
16070          * Supported properties are: ok, cancel, yes and no.
16071          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
16072          * @type Object
16073          */
16074         buttonText : {
16075             ok : "OK",
16076             cancel : "Cancel",
16077             yes : "Yes",
16078             no : "No"
16079         }
16080     };
16081 }();
16082
16083 /**
16084  * Shorthand for {@link Roo.MessageBox}
16085  */
16086 Roo.Msg = Roo.MessageBox;/*
16087  * Based on:
16088  * Ext JS Library 1.1.1
16089  * Copyright(c) 2006-2007, Ext JS, LLC.
16090  *
16091  * Originally Released Under LGPL - original licence link has changed is not relivant.
16092  *
16093  * Fork - LGPL
16094  * <script type="text/javascript">
16095  */
16096 /**
16097  * @class Roo.QuickTips
16098  * Provides attractive and customizable tooltips for any element.
16099  * @singleton
16100  */
16101 Roo.QuickTips = function(){
16102     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
16103     var ce, bd, xy, dd;
16104     var visible = false, disabled = true, inited = false;
16105     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
16106     
16107     var onOver = function(e){
16108         if(disabled){
16109             return;
16110         }
16111         var t = e.getTarget();
16112         if(!t || t.nodeType !== 1 || t == document || t == document.body){
16113             return;
16114         }
16115         if(ce && t == ce.el){
16116             clearTimeout(hideProc);
16117             return;
16118         }
16119         if(t && tagEls[t.id]){
16120             tagEls[t.id].el = t;
16121             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
16122             return;
16123         }
16124         var ttp, et = Roo.fly(t);
16125         var ns = cfg.namespace;
16126         if(tm.interceptTitles && t.title){
16127             ttp = t.title;
16128             t.qtip = ttp;
16129             t.removeAttribute("title");
16130             e.preventDefault();
16131         }else{
16132             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
16133         }
16134         if(ttp){
16135             showProc = show.defer(tm.showDelay, tm, [{
16136                 el: t, 
16137                 text: ttp, 
16138                 width: et.getAttributeNS(ns, cfg.width),
16139                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
16140                 title: et.getAttributeNS(ns, cfg.title),
16141                     cls: et.getAttributeNS(ns, cfg.cls)
16142             }]);
16143         }
16144     };
16145     
16146     var onOut = function(e){
16147         clearTimeout(showProc);
16148         var t = e.getTarget();
16149         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
16150             hideProc = setTimeout(hide, tm.hideDelay);
16151         }
16152     };
16153     
16154     var onMove = function(e){
16155         if(disabled){
16156             return;
16157         }
16158         xy = e.getXY();
16159         xy[1] += 18;
16160         if(tm.trackMouse && ce){
16161             el.setXY(xy);
16162         }
16163     };
16164     
16165     var onDown = function(e){
16166         clearTimeout(showProc);
16167         clearTimeout(hideProc);
16168         if(!e.within(el)){
16169             if(tm.hideOnClick){
16170                 hide();
16171                 tm.disable();
16172                 tm.enable.defer(100, tm);
16173             }
16174         }
16175     };
16176     
16177     var getPad = function(){
16178         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
16179     };
16180
16181     var show = function(o){
16182         if(disabled){
16183             return;
16184         }
16185         clearTimeout(dismissProc);
16186         ce = o;
16187         if(removeCls){ // in case manually hidden
16188             el.removeClass(removeCls);
16189             removeCls = null;
16190         }
16191         if(ce.cls){
16192             el.addClass(ce.cls);
16193             removeCls = ce.cls;
16194         }
16195         if(ce.title){
16196             tipTitle.update(ce.title);
16197             tipTitle.show();
16198         }else{
16199             tipTitle.update('');
16200             tipTitle.hide();
16201         }
16202         el.dom.style.width  = tm.maxWidth+'px';
16203         //tipBody.dom.style.width = '';
16204         tipBodyText.update(o.text);
16205         var p = getPad(), w = ce.width;
16206         if(!w){
16207             var td = tipBodyText.dom;
16208             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
16209             if(aw > tm.maxWidth){
16210                 w = tm.maxWidth;
16211             }else if(aw < tm.minWidth){
16212                 w = tm.minWidth;
16213             }else{
16214                 w = aw;
16215             }
16216         }
16217         //tipBody.setWidth(w);
16218         el.setWidth(parseInt(w, 10) + p);
16219         if(ce.autoHide === false){
16220             close.setDisplayed(true);
16221             if(dd){
16222                 dd.unlock();
16223             }
16224         }else{
16225             close.setDisplayed(false);
16226             if(dd){
16227                 dd.lock();
16228             }
16229         }
16230         if(xy){
16231             el.avoidY = xy[1]-18;
16232             el.setXY(xy);
16233         }
16234         if(tm.animate){
16235             el.setOpacity(.1);
16236             el.setStyle("visibility", "visible");
16237             el.fadeIn({callback: afterShow});
16238         }else{
16239             afterShow();
16240         }
16241     };
16242     
16243     var afterShow = function(){
16244         if(ce){
16245             el.show();
16246             esc.enable();
16247             if(tm.autoDismiss && ce.autoHide !== false){
16248                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
16249             }
16250         }
16251     };
16252     
16253     var hide = function(noanim){
16254         clearTimeout(dismissProc);
16255         clearTimeout(hideProc);
16256         ce = null;
16257         if(el.isVisible()){
16258             esc.disable();
16259             if(noanim !== true && tm.animate){
16260                 el.fadeOut({callback: afterHide});
16261             }else{
16262                 afterHide();
16263             } 
16264         }
16265     };
16266     
16267     var afterHide = function(){
16268         el.hide();
16269         if(removeCls){
16270             el.removeClass(removeCls);
16271             removeCls = null;
16272         }
16273     };
16274     
16275     return {
16276         /**
16277         * @cfg {Number} minWidth
16278         * The minimum width of the quick tip (defaults to 40)
16279         */
16280        minWidth : 40,
16281         /**
16282         * @cfg {Number} maxWidth
16283         * The maximum width of the quick tip (defaults to 300)
16284         */
16285        maxWidth : 300,
16286         /**
16287         * @cfg {Boolean} interceptTitles
16288         * True to automatically use the element's DOM title value if available (defaults to false)
16289         */
16290        interceptTitles : false,
16291         /**
16292         * @cfg {Boolean} trackMouse
16293         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
16294         */
16295        trackMouse : false,
16296         /**
16297         * @cfg {Boolean} hideOnClick
16298         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
16299         */
16300        hideOnClick : true,
16301         /**
16302         * @cfg {Number} showDelay
16303         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
16304         */
16305        showDelay : 500,
16306         /**
16307         * @cfg {Number} hideDelay
16308         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
16309         */
16310        hideDelay : 200,
16311         /**
16312         * @cfg {Boolean} autoHide
16313         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
16314         * Used in conjunction with hideDelay.
16315         */
16316        autoHide : true,
16317         /**
16318         * @cfg {Boolean}
16319         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
16320         * (defaults to true).  Used in conjunction with autoDismissDelay.
16321         */
16322        autoDismiss : true,
16323         /**
16324         * @cfg {Number}
16325         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
16326         */
16327        autoDismissDelay : 5000,
16328        /**
16329         * @cfg {Boolean} animate
16330         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
16331         */
16332        animate : false,
16333
16334        /**
16335         * @cfg {String} title
16336         * Title text to display (defaults to '').  This can be any valid HTML markup.
16337         */
16338         title: '',
16339        /**
16340         * @cfg {String} text
16341         * Body text to display (defaults to '').  This can be any valid HTML markup.
16342         */
16343         text : '',
16344        /**
16345         * @cfg {String} cls
16346         * A CSS class to apply to the base quick tip element (defaults to '').
16347         */
16348         cls : '',
16349        /**
16350         * @cfg {Number} width
16351         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
16352         * minWidth or maxWidth.
16353         */
16354         width : null,
16355
16356     /**
16357      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
16358      * or display QuickTips in a page.
16359      */
16360        init : function(){
16361           tm = Roo.QuickTips;
16362           cfg = tm.tagConfig;
16363           if(!inited){
16364               if(!Roo.isReady){ // allow calling of init() before onReady
16365                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
16366                   return;
16367               }
16368               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
16369               el.fxDefaults = {stopFx: true};
16370               // maximum custom styling
16371               //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>');
16372               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>');              
16373               tipTitle = el.child('h3');
16374               tipTitle.enableDisplayMode("block");
16375               tipBody = el.child('div.x-tip-bd');
16376               tipBodyText = el.child('div.x-tip-bd-inner');
16377               //bdLeft = el.child('div.x-tip-bd-left');
16378               //bdRight = el.child('div.x-tip-bd-right');
16379               close = el.child('div.x-tip-close');
16380               close.enableDisplayMode("block");
16381               close.on("click", hide);
16382               var d = Roo.get(document);
16383               d.on("mousedown", onDown);
16384               d.on("mouseover", onOver);
16385               d.on("mouseout", onOut);
16386               d.on("mousemove", onMove);
16387               esc = d.addKeyListener(27, hide);
16388               esc.disable();
16389               if(Roo.dd.DD){
16390                   dd = el.initDD("default", null, {
16391                       onDrag : function(){
16392                           el.sync();  
16393                       }
16394                   });
16395                   dd.setHandleElId(tipTitle.id);
16396                   dd.lock();
16397               }
16398               inited = true;
16399           }
16400           this.enable(); 
16401        },
16402
16403     /**
16404      * Configures a new quick tip instance and assigns it to a target element.  The following config options
16405      * are supported:
16406      * <pre>
16407 Property    Type                   Description
16408 ----------  ---------------------  ------------------------------------------------------------------------
16409 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
16410      * </ul>
16411      * @param {Object} config The config object
16412      */
16413        register : function(config){
16414            var cs = config instanceof Array ? config : arguments;
16415            for(var i = 0, len = cs.length; i < len; i++) {
16416                var c = cs[i];
16417                var target = c.target;
16418                if(target){
16419                    if(target instanceof Array){
16420                        for(var j = 0, jlen = target.length; j < jlen; j++){
16421                            tagEls[target[j]] = c;
16422                        }
16423                    }else{
16424                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
16425                    }
16426                }
16427            }
16428        },
16429
16430     /**
16431      * Removes this quick tip from its element and destroys it.
16432      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
16433      */
16434        unregister : function(el){
16435            delete tagEls[Roo.id(el)];
16436        },
16437
16438     /**
16439      * Enable this quick tip.
16440      */
16441        enable : function(){
16442            if(inited && disabled){
16443                locks.pop();
16444                if(locks.length < 1){
16445                    disabled = false;
16446                }
16447            }
16448        },
16449
16450     /**
16451      * Disable this quick tip.
16452      */
16453        disable : function(){
16454           disabled = true;
16455           clearTimeout(showProc);
16456           clearTimeout(hideProc);
16457           clearTimeout(dismissProc);
16458           if(ce){
16459               hide(true);
16460           }
16461           locks.push(1);
16462        },
16463
16464     /**
16465      * Returns true if the quick tip is enabled, else false.
16466      */
16467        isEnabled : function(){
16468             return !disabled;
16469        },
16470
16471         // private
16472        tagConfig : {
16473            namespace : "ext",
16474            attribute : "qtip",
16475            width : "width",
16476            target : "target",
16477            title : "qtitle",
16478            hide : "hide",
16479            cls : "qclass"
16480        }
16481    };
16482 }();
16483
16484 // backwards compat
16485 Roo.QuickTips.tips = Roo.QuickTips.register;/*
16486  * Based on:
16487  * Ext JS Library 1.1.1
16488  * Copyright(c) 2006-2007, Ext JS, LLC.
16489  *
16490  * Originally Released Under LGPL - original licence link has changed is not relivant.
16491  *
16492  * Fork - LGPL
16493  * <script type="text/javascript">
16494  */
16495  
16496
16497 /**
16498  * @class Roo.tree.TreePanel
16499  * @extends Roo.data.Tree
16500
16501  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
16502  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
16503  * @cfg {Boolean} enableDD true to enable drag and drop
16504  * @cfg {Boolean} enableDrag true to enable just drag
16505  * @cfg {Boolean} enableDrop true to enable just drop
16506  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
16507  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
16508  * @cfg {String} ddGroup The DD group this TreePanel belongs to
16509  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
16510  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
16511  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
16512  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
16513  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
16514  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
16515  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
16516  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
16517  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
16518  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
16519  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
16520  * @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>
16521  * @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>
16522  * 
16523  * @constructor
16524  * @param {String/HTMLElement/Element} el The container element
16525  * @param {Object} config
16526  */
16527 Roo.tree.TreePanel = function(el, config){
16528     var root = false;
16529     var loader = false;
16530     if (config.root) {
16531         root = config.root;
16532         delete config.root;
16533     }
16534     if (config.loader) {
16535         loader = config.loader;
16536         delete config.loader;
16537     }
16538     
16539     Roo.apply(this, config);
16540     Roo.tree.TreePanel.superclass.constructor.call(this);
16541     this.el = Roo.get(el);
16542     this.el.addClass('x-tree');
16543     //console.log(root);
16544     if (root) {
16545         this.setRootNode( Roo.factory(root, Roo.tree));
16546     }
16547     if (loader) {
16548         this.loader = Roo.factory(loader, Roo.tree);
16549     }
16550    /**
16551     * Read-only. The id of the container element becomes this TreePanel's id.
16552     */
16553     this.id = this.el.id;
16554     this.addEvents({
16555         /**
16556         * @event beforeload
16557         * Fires before a node is loaded, return false to cancel
16558         * @param {Node} node The node being loaded
16559         */
16560         "beforeload" : true,
16561         /**
16562         * @event load
16563         * Fires when a node is loaded
16564         * @param {Node} node The node that was loaded
16565         */
16566         "load" : true,
16567         /**
16568         * @event textchange
16569         * Fires when the text for a node is changed
16570         * @param {Node} node The node
16571         * @param {String} text The new text
16572         * @param {String} oldText The old text
16573         */
16574         "textchange" : true,
16575         /**
16576         * @event beforeexpand
16577         * Fires before a node is expanded, return false to cancel.
16578         * @param {Node} node The node
16579         * @param {Boolean} deep
16580         * @param {Boolean} anim
16581         */
16582         "beforeexpand" : true,
16583         /**
16584         * @event beforecollapse
16585         * Fires before a node is collapsed, return false to cancel.
16586         * @param {Node} node The node
16587         * @param {Boolean} deep
16588         * @param {Boolean} anim
16589         */
16590         "beforecollapse" : true,
16591         /**
16592         * @event expand
16593         * Fires when a node is expanded
16594         * @param {Node} node The node
16595         */
16596         "expand" : true,
16597         /**
16598         * @event disabledchange
16599         * Fires when the disabled status of a node changes
16600         * @param {Node} node The node
16601         * @param {Boolean} disabled
16602         */
16603         "disabledchange" : true,
16604         /**
16605         * @event collapse
16606         * Fires when a node is collapsed
16607         * @param {Node} node The node
16608         */
16609         "collapse" : true,
16610         /**
16611         * @event beforeclick
16612         * Fires before click processing on a node. Return false to cancel the default action.
16613         * @param {Node} node The node
16614         * @param {Roo.EventObject} e The event object
16615         */
16616         "beforeclick":true,
16617         /**
16618         * @event checkchange
16619         * Fires when a node with a checkbox's checked property changes
16620         * @param {Node} this This node
16621         * @param {Boolean} checked
16622         */
16623         "checkchange":true,
16624         /**
16625         * @event click
16626         * Fires when a node is clicked
16627         * @param {Node} node The node
16628         * @param {Roo.EventObject} e The event object
16629         */
16630         "click":true,
16631         /**
16632         * @event dblclick
16633         * Fires when a node is double clicked
16634         * @param {Node} node The node
16635         * @param {Roo.EventObject} e The event object
16636         */
16637         "dblclick":true,
16638         /**
16639         * @event contextmenu
16640         * Fires when a node is right clicked
16641         * @param {Node} node The node
16642         * @param {Roo.EventObject} e The event object
16643         */
16644         "contextmenu":true,
16645         /**
16646         * @event beforechildrenrendered
16647         * Fires right before the child nodes for a node are rendered
16648         * @param {Node} node The node
16649         */
16650         "beforechildrenrendered":true,
16651         /**
16652         * @event startdrag
16653         * Fires when a node starts being dragged
16654         * @param {Roo.tree.TreePanel} this
16655         * @param {Roo.tree.TreeNode} node
16656         * @param {event} e The raw browser event
16657         */ 
16658        "startdrag" : true,
16659        /**
16660         * @event enddrag
16661         * Fires when a drag operation is complete
16662         * @param {Roo.tree.TreePanel} this
16663         * @param {Roo.tree.TreeNode} node
16664         * @param {event} e The raw browser event
16665         */
16666        "enddrag" : true,
16667        /**
16668         * @event dragdrop
16669         * Fires when a dragged node is dropped on a valid DD target
16670         * @param {Roo.tree.TreePanel} this
16671         * @param {Roo.tree.TreeNode} node
16672         * @param {DD} dd The dd it was dropped on
16673         * @param {event} e The raw browser event
16674         */
16675        "dragdrop" : true,
16676        /**
16677         * @event beforenodedrop
16678         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16679         * passed to handlers has the following properties:<br />
16680         * <ul style="padding:5px;padding-left:16px;">
16681         * <li>tree - The TreePanel</li>
16682         * <li>target - The node being targeted for the drop</li>
16683         * <li>data - The drag data from the drag source</li>
16684         * <li>point - The point of the drop - append, above or below</li>
16685         * <li>source - The drag source</li>
16686         * <li>rawEvent - Raw mouse event</li>
16687         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16688         * to be inserted by setting them on this object.</li>
16689         * <li>cancel - Set this to true to cancel the drop.</li>
16690         * </ul>
16691         * @param {Object} dropEvent
16692         */
16693        "beforenodedrop" : true,
16694        /**
16695         * @event nodedrop
16696         * Fires after a DD object is dropped on a node in this tree. The dropEvent
16697         * passed to handlers has the following properties:<br />
16698         * <ul style="padding:5px;padding-left:16px;">
16699         * <li>tree - The TreePanel</li>
16700         * <li>target - The node being targeted for the drop</li>
16701         * <li>data - The drag data from the drag source</li>
16702         * <li>point - The point of the drop - append, above or below</li>
16703         * <li>source - The drag source</li>
16704         * <li>rawEvent - Raw mouse event</li>
16705         * <li>dropNode - Dropped node(s).</li>
16706         * </ul>
16707         * @param {Object} dropEvent
16708         */
16709        "nodedrop" : true,
16710         /**
16711         * @event nodedragover
16712         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16713         * passed to handlers has the following properties:<br />
16714         * <ul style="padding:5px;padding-left:16px;">
16715         * <li>tree - The TreePanel</li>
16716         * <li>target - The node being targeted for the drop</li>
16717         * <li>data - The drag data from the drag source</li>
16718         * <li>point - The point of the drop - append, above or below</li>
16719         * <li>source - The drag source</li>
16720         * <li>rawEvent - Raw mouse event</li>
16721         * <li>dropNode - Drop node(s) provided by the source.</li>
16722         * <li>cancel - Set this to true to signal drop not allowed.</li>
16723         * </ul>
16724         * @param {Object} dragOverEvent
16725         */
16726        "nodedragover" : true
16727         
16728     });
16729     if(this.singleExpand){
16730        this.on("beforeexpand", this.restrictExpand, this);
16731     }
16732     if (this.editor) {
16733         this.editor.tree = this;
16734         this.editor = Roo.factory(this.editor, Roo.tree);
16735     }
16736     
16737     if (this.selModel) {
16738         this.selModel = Roo.factory(this.selModel, Roo.tree);
16739     }
16740    
16741 };
16742 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16743     rootVisible : true,
16744     animate: Roo.enableFx,
16745     lines : true,
16746     enableDD : false,
16747     hlDrop : Roo.enableFx,
16748   
16749     renderer: false,
16750     
16751     rendererTip: false,
16752     // private
16753     restrictExpand : function(node){
16754         var p = node.parentNode;
16755         if(p){
16756             if(p.expandedChild && p.expandedChild.parentNode == p){
16757                 p.expandedChild.collapse();
16758             }
16759             p.expandedChild = node;
16760         }
16761     },
16762
16763     // private override
16764     setRootNode : function(node){
16765         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16766         if(!this.rootVisible){
16767             node.ui = new Roo.tree.RootTreeNodeUI(node);
16768         }
16769         return node;
16770     },
16771
16772     /**
16773      * Returns the container element for this TreePanel
16774      */
16775     getEl : function(){
16776         return this.el;
16777     },
16778
16779     /**
16780      * Returns the default TreeLoader for this TreePanel
16781      */
16782     getLoader : function(){
16783         return this.loader;
16784     },
16785
16786     /**
16787      * Expand all nodes
16788      */
16789     expandAll : function(){
16790         this.root.expand(true);
16791     },
16792
16793     /**
16794      * Collapse all nodes
16795      */
16796     collapseAll : function(){
16797         this.root.collapse(true);
16798     },
16799
16800     /**
16801      * Returns the selection model used by this TreePanel
16802      */
16803     getSelectionModel : function(){
16804         if(!this.selModel){
16805             this.selModel = new Roo.tree.DefaultSelectionModel();
16806         }
16807         return this.selModel;
16808     },
16809
16810     /**
16811      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16812      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16813      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16814      * @return {Array}
16815      */
16816     getChecked : function(a, startNode){
16817         startNode = startNode || this.root;
16818         var r = [];
16819         var f = function(){
16820             if(this.attributes.checked){
16821                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16822             }
16823         }
16824         startNode.cascade(f);
16825         return r;
16826     },
16827
16828     /**
16829      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16830      * @param {String} path
16831      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16832      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16833      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16834      */
16835     expandPath : function(path, attr, callback){
16836         attr = attr || "id";
16837         var keys = path.split(this.pathSeparator);
16838         var curNode = this.root;
16839         if(curNode.attributes[attr] != keys[1]){ // invalid root
16840             if(callback){
16841                 callback(false, null);
16842             }
16843             return;
16844         }
16845         var index = 1;
16846         var f = function(){
16847             if(++index == keys.length){
16848                 if(callback){
16849                     callback(true, curNode);
16850                 }
16851                 return;
16852             }
16853             var c = curNode.findChild(attr, keys[index]);
16854             if(!c){
16855                 if(callback){
16856                     callback(false, curNode);
16857                 }
16858                 return;
16859             }
16860             curNode = c;
16861             c.expand(false, false, f);
16862         };
16863         curNode.expand(false, false, f);
16864     },
16865
16866     /**
16867      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16868      * @param {String} path
16869      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16870      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16871      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16872      */
16873     selectPath : function(path, attr, callback){
16874         attr = attr || "id";
16875         var keys = path.split(this.pathSeparator);
16876         var v = keys.pop();
16877         if(keys.length > 0){
16878             var f = function(success, node){
16879                 if(success && node){
16880                     var n = node.findChild(attr, v);
16881                     if(n){
16882                         n.select();
16883                         if(callback){
16884                             callback(true, n);
16885                         }
16886                     }else if(callback){
16887                         callback(false, n);
16888                     }
16889                 }else{
16890                     if(callback){
16891                         callback(false, n);
16892                     }
16893                 }
16894             };
16895             this.expandPath(keys.join(this.pathSeparator), attr, f);
16896         }else{
16897             this.root.select();
16898             if(callback){
16899                 callback(true, this.root);
16900             }
16901         }
16902     },
16903
16904     getTreeEl : function(){
16905         return this.el;
16906     },
16907
16908     /**
16909      * Trigger rendering of this TreePanel
16910      */
16911     render : function(){
16912         if (this.innerCt) {
16913             return this; // stop it rendering more than once!!
16914         }
16915         
16916         this.innerCt = this.el.createChild({tag:"ul",
16917                cls:"x-tree-root-ct " +
16918                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
16919
16920         if(this.containerScroll){
16921             Roo.dd.ScrollManager.register(this.el);
16922         }
16923         if((this.enableDD || this.enableDrop) && !this.dropZone){
16924            /**
16925             * The dropZone used by this tree if drop is enabled
16926             * @type Roo.tree.TreeDropZone
16927             */
16928              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
16929                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
16930            });
16931         }
16932         if((this.enableDD || this.enableDrag) && !this.dragZone){
16933            /**
16934             * The dragZone used by this tree if drag is enabled
16935             * @type Roo.tree.TreeDragZone
16936             */
16937             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
16938                ddGroup: this.ddGroup || "TreeDD",
16939                scroll: this.ddScroll
16940            });
16941         }
16942         this.getSelectionModel().init(this);
16943         if (!this.root) {
16944             Roo.log("ROOT not set in tree");
16945             return this;
16946         }
16947         this.root.render();
16948         if(!this.rootVisible){
16949             this.root.renderChildren();
16950         }
16951         return this;
16952     }
16953 });/*
16954  * Based on:
16955  * Ext JS Library 1.1.1
16956  * Copyright(c) 2006-2007, Ext JS, LLC.
16957  *
16958  * Originally Released Under LGPL - original licence link has changed is not relivant.
16959  *
16960  * Fork - LGPL
16961  * <script type="text/javascript">
16962  */
16963  
16964
16965 /**
16966  * @class Roo.tree.DefaultSelectionModel
16967  * @extends Roo.util.Observable
16968  * The default single selection for a TreePanel.
16969  * @param {Object} cfg Configuration
16970  */
16971 Roo.tree.DefaultSelectionModel = function(cfg){
16972    this.selNode = null;
16973    
16974    
16975    
16976    this.addEvents({
16977        /**
16978         * @event selectionchange
16979         * Fires when the selected node changes
16980         * @param {DefaultSelectionModel} this
16981         * @param {TreeNode} node the new selection
16982         */
16983        "selectionchange" : true,
16984
16985        /**
16986         * @event beforeselect
16987         * Fires before the selected node changes, return false to cancel the change
16988         * @param {DefaultSelectionModel} this
16989         * @param {TreeNode} node the new selection
16990         * @param {TreeNode} node the old selection
16991         */
16992        "beforeselect" : true
16993    });
16994    
16995     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
16996 };
16997
16998 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
16999     init : function(tree){
17000         this.tree = tree;
17001         tree.getTreeEl().on("keydown", this.onKeyDown, this);
17002         tree.on("click", this.onNodeClick, this);
17003     },
17004     
17005     onNodeClick : function(node, e){
17006         if (e.ctrlKey && this.selNode == node)  {
17007             this.unselect(node);
17008             return;
17009         }
17010         this.select(node);
17011     },
17012     
17013     /**
17014      * Select a node.
17015      * @param {TreeNode} node The node to select
17016      * @return {TreeNode} The selected node
17017      */
17018     select : function(node){
17019         var last = this.selNode;
17020         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
17021             if(last){
17022                 last.ui.onSelectedChange(false);
17023             }
17024             this.selNode = node;
17025             node.ui.onSelectedChange(true);
17026             this.fireEvent("selectionchange", this, node, last);
17027         }
17028         return node;
17029     },
17030     
17031     /**
17032      * Deselect a node.
17033      * @param {TreeNode} node The node to unselect
17034      */
17035     unselect : function(node){
17036         if(this.selNode == node){
17037             this.clearSelections();
17038         }    
17039     },
17040     
17041     /**
17042      * Clear all selections
17043      */
17044     clearSelections : function(){
17045         var n = this.selNode;
17046         if(n){
17047             n.ui.onSelectedChange(false);
17048             this.selNode = null;
17049             this.fireEvent("selectionchange", this, null);
17050         }
17051         return n;
17052     },
17053     
17054     /**
17055      * Get the selected node
17056      * @return {TreeNode} The selected node
17057      */
17058     getSelectedNode : function(){
17059         return this.selNode;    
17060     },
17061     
17062     /**
17063      * Returns true if the node is selected
17064      * @param {TreeNode} node The node to check
17065      * @return {Boolean}
17066      */
17067     isSelected : function(node){
17068         return this.selNode == node;  
17069     },
17070
17071     /**
17072      * Selects the node above the selected node in the tree, intelligently walking the nodes
17073      * @return TreeNode The new selection
17074      */
17075     selectPrevious : function(){
17076         var s = this.selNode || this.lastSelNode;
17077         if(!s){
17078             return null;
17079         }
17080         var ps = s.previousSibling;
17081         if(ps){
17082             if(!ps.isExpanded() || ps.childNodes.length < 1){
17083                 return this.select(ps);
17084             } else{
17085                 var lc = ps.lastChild;
17086                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
17087                     lc = lc.lastChild;
17088                 }
17089                 return this.select(lc);
17090             }
17091         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
17092             return this.select(s.parentNode);
17093         }
17094         return null;
17095     },
17096
17097     /**
17098      * Selects the node above the selected node in the tree, intelligently walking the nodes
17099      * @return TreeNode The new selection
17100      */
17101     selectNext : function(){
17102         var s = this.selNode || this.lastSelNode;
17103         if(!s){
17104             return null;
17105         }
17106         if(s.firstChild && s.isExpanded()){
17107              return this.select(s.firstChild);
17108          }else if(s.nextSibling){
17109              return this.select(s.nextSibling);
17110          }else if(s.parentNode){
17111             var newS = null;
17112             s.parentNode.bubble(function(){
17113                 if(this.nextSibling){
17114                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
17115                     return false;
17116                 }
17117             });
17118             return newS;
17119          }
17120         return null;
17121     },
17122
17123     onKeyDown : function(e){
17124         var s = this.selNode || this.lastSelNode;
17125         // undesirable, but required
17126         var sm = this;
17127         if(!s){
17128             return;
17129         }
17130         var k = e.getKey();
17131         switch(k){
17132              case e.DOWN:
17133                  e.stopEvent();
17134                  this.selectNext();
17135              break;
17136              case e.UP:
17137                  e.stopEvent();
17138                  this.selectPrevious();
17139              break;
17140              case e.RIGHT:
17141                  e.preventDefault();
17142                  if(s.hasChildNodes()){
17143                      if(!s.isExpanded()){
17144                          s.expand();
17145                      }else if(s.firstChild){
17146                          this.select(s.firstChild, e);
17147                      }
17148                  }
17149              break;
17150              case e.LEFT:
17151                  e.preventDefault();
17152                  if(s.hasChildNodes() && s.isExpanded()){
17153                      s.collapse();
17154                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
17155                      this.select(s.parentNode, e);
17156                  }
17157              break;
17158         };
17159     }
17160 });
17161
17162 /**
17163  * @class Roo.tree.MultiSelectionModel
17164  * @extends Roo.util.Observable
17165  * Multi selection for a TreePanel.
17166  * @param {Object} cfg Configuration
17167  */
17168 Roo.tree.MultiSelectionModel = function(){
17169    this.selNodes = [];
17170    this.selMap = {};
17171    this.addEvents({
17172        /**
17173         * @event selectionchange
17174         * Fires when the selected nodes change
17175         * @param {MultiSelectionModel} this
17176         * @param {Array} nodes Array of the selected nodes
17177         */
17178        "selectionchange" : true
17179    });
17180    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
17181    
17182 };
17183
17184 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
17185     init : function(tree){
17186         this.tree = tree;
17187         tree.getTreeEl().on("keydown", this.onKeyDown, this);
17188         tree.on("click", this.onNodeClick, this);
17189     },
17190     
17191     onNodeClick : function(node, e){
17192         this.select(node, e, e.ctrlKey);
17193     },
17194     
17195     /**
17196      * Select a node.
17197      * @param {TreeNode} node The node to select
17198      * @param {EventObject} e (optional) An event associated with the selection
17199      * @param {Boolean} keepExisting True to retain existing selections
17200      * @return {TreeNode} The selected node
17201      */
17202     select : function(node, e, keepExisting){
17203         if(keepExisting !== true){
17204             this.clearSelections(true);
17205         }
17206         if(this.isSelected(node)){
17207             this.lastSelNode = node;
17208             return node;
17209         }
17210         this.selNodes.push(node);
17211         this.selMap[node.id] = node;
17212         this.lastSelNode = node;
17213         node.ui.onSelectedChange(true);
17214         this.fireEvent("selectionchange", this, this.selNodes);
17215         return node;
17216     },
17217     
17218     /**
17219      * Deselect a node.
17220      * @param {TreeNode} node The node to unselect
17221      */
17222     unselect : function(node){
17223         if(this.selMap[node.id]){
17224             node.ui.onSelectedChange(false);
17225             var sn = this.selNodes;
17226             var index = -1;
17227             if(sn.indexOf){
17228                 index = sn.indexOf(node);
17229             }else{
17230                 for(var i = 0, len = sn.length; i < len; i++){
17231                     if(sn[i] == node){
17232                         index = i;
17233                         break;
17234                     }
17235                 }
17236             }
17237             if(index != -1){
17238                 this.selNodes.splice(index, 1);
17239             }
17240             delete this.selMap[node.id];
17241             this.fireEvent("selectionchange", this, this.selNodes);
17242         }
17243     },
17244     
17245     /**
17246      * Clear all selections
17247      */
17248     clearSelections : function(suppressEvent){
17249         var sn = this.selNodes;
17250         if(sn.length > 0){
17251             for(var i = 0, len = sn.length; i < len; i++){
17252                 sn[i].ui.onSelectedChange(false);
17253             }
17254             this.selNodes = [];
17255             this.selMap = {};
17256             if(suppressEvent !== true){
17257                 this.fireEvent("selectionchange", this, this.selNodes);
17258             }
17259         }
17260     },
17261     
17262     /**
17263      * Returns true if the node is selected
17264      * @param {TreeNode} node The node to check
17265      * @return {Boolean}
17266      */
17267     isSelected : function(node){
17268         return this.selMap[node.id] ? true : false;  
17269     },
17270     
17271     /**
17272      * Returns an array of the selected nodes
17273      * @return {Array}
17274      */
17275     getSelectedNodes : function(){
17276         return this.selNodes;    
17277     },
17278
17279     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
17280
17281     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
17282
17283     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
17284 });/*
17285  * Based on:
17286  * Ext JS Library 1.1.1
17287  * Copyright(c) 2006-2007, Ext JS, LLC.
17288  *
17289  * Originally Released Under LGPL - original licence link has changed is not relivant.
17290  *
17291  * Fork - LGPL
17292  * <script type="text/javascript">
17293  */
17294  
17295 /**
17296  * @class Roo.tree.TreeNode
17297  * @extends Roo.data.Node
17298  * @cfg {String} text The text for this node
17299  * @cfg {Boolean} expanded true to start the node expanded
17300  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
17301  * @cfg {Boolean} allowDrop false if this node cannot be drop on
17302  * @cfg {Boolean} disabled true to start the node disabled
17303  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
17304  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
17305  * @cfg {String} cls A css class to be added to the node
17306  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
17307  * @cfg {String} href URL of the link used for the node (defaults to #)
17308  * @cfg {String} hrefTarget target frame for the link
17309  * @cfg {String} qtip An Ext QuickTip for the node
17310  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
17311  * @cfg {Boolean} singleClickExpand True for single click expand on this node
17312  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
17313  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
17314  * (defaults to undefined with no checkbox rendered)
17315  * @constructor
17316  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
17317  */
17318 Roo.tree.TreeNode = function(attributes){
17319     attributes = attributes || {};
17320     if(typeof attributes == "string"){
17321         attributes = {text: attributes};
17322     }
17323     this.childrenRendered = false;
17324     this.rendered = false;
17325     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
17326     this.expanded = attributes.expanded === true;
17327     this.isTarget = attributes.isTarget !== false;
17328     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
17329     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
17330
17331     /**
17332      * Read-only. The text for this node. To change it use setText().
17333      * @type String
17334      */
17335     this.text = attributes.text;
17336     /**
17337      * True if this node is disabled.
17338      * @type Boolean
17339      */
17340     this.disabled = attributes.disabled === true;
17341
17342     this.addEvents({
17343         /**
17344         * @event textchange
17345         * Fires when the text for this node is changed
17346         * @param {Node} this This node
17347         * @param {String} text The new text
17348         * @param {String} oldText The old text
17349         */
17350         "textchange" : true,
17351         /**
17352         * @event beforeexpand
17353         * Fires before this node is expanded, return false to cancel.
17354         * @param {Node} this This node
17355         * @param {Boolean} deep
17356         * @param {Boolean} anim
17357         */
17358         "beforeexpand" : true,
17359         /**
17360         * @event beforecollapse
17361         * Fires before this node is collapsed, return false to cancel.
17362         * @param {Node} this This node
17363         * @param {Boolean} deep
17364         * @param {Boolean} anim
17365         */
17366         "beforecollapse" : true,
17367         /**
17368         * @event expand
17369         * Fires when this node is expanded
17370         * @param {Node} this This node
17371         */
17372         "expand" : true,
17373         /**
17374         * @event disabledchange
17375         * Fires when the disabled status of this node changes
17376         * @param {Node} this This node
17377         * @param {Boolean} disabled
17378         */
17379         "disabledchange" : true,
17380         /**
17381         * @event collapse
17382         * Fires when this node is collapsed
17383         * @param {Node} this This node
17384         */
17385         "collapse" : true,
17386         /**
17387         * @event beforeclick
17388         * Fires before click processing. Return false to cancel the default action.
17389         * @param {Node} this This node
17390         * @param {Roo.EventObject} e The event object
17391         */
17392         "beforeclick":true,
17393         /**
17394         * @event checkchange
17395         * Fires when a node with a checkbox's checked property changes
17396         * @param {Node} this This node
17397         * @param {Boolean} checked
17398         */
17399         "checkchange":true,
17400         /**
17401         * @event click
17402         * Fires when this node is clicked
17403         * @param {Node} this This node
17404         * @param {Roo.EventObject} e The event object
17405         */
17406         "click":true,
17407         /**
17408         * @event dblclick
17409         * Fires when this node is double clicked
17410         * @param {Node} this This node
17411         * @param {Roo.EventObject} e The event object
17412         */
17413         "dblclick":true,
17414         /**
17415         * @event contextmenu
17416         * Fires when this node is right clicked
17417         * @param {Node} this This node
17418         * @param {Roo.EventObject} e The event object
17419         */
17420         "contextmenu":true,
17421         /**
17422         * @event beforechildrenrendered
17423         * Fires right before the child nodes for this node are rendered
17424         * @param {Node} this This node
17425         */
17426         "beforechildrenrendered":true
17427     });
17428
17429     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
17430
17431     /**
17432      * Read-only. The UI for this node
17433      * @type TreeNodeUI
17434      */
17435     this.ui = new uiClass(this);
17436     
17437     // finally support items[]
17438     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
17439         return;
17440     }
17441     
17442     
17443     Roo.each(this.attributes.items, function(c) {
17444         this.appendChild(Roo.factory(c,Roo.Tree));
17445     }, this);
17446     delete this.attributes.items;
17447     
17448     
17449     
17450 };
17451 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
17452     preventHScroll: true,
17453     /**
17454      * Returns true if this node is expanded
17455      * @return {Boolean}
17456      */
17457     isExpanded : function(){
17458         return this.expanded;
17459     },
17460
17461     /**
17462      * Returns the UI object for this node
17463      * @return {TreeNodeUI}
17464      */
17465     getUI : function(){
17466         return this.ui;
17467     },
17468
17469     // private override
17470     setFirstChild : function(node){
17471         var of = this.firstChild;
17472         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
17473         if(this.childrenRendered && of && node != of){
17474             of.renderIndent(true, true);
17475         }
17476         if(this.rendered){
17477             this.renderIndent(true, true);
17478         }
17479     },
17480
17481     // private override
17482     setLastChild : function(node){
17483         var ol = this.lastChild;
17484         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
17485         if(this.childrenRendered && ol && node != ol){
17486             ol.renderIndent(true, true);
17487         }
17488         if(this.rendered){
17489             this.renderIndent(true, true);
17490         }
17491     },
17492
17493     // these methods are overridden to provide lazy rendering support
17494     // private override
17495     appendChild : function()
17496     {
17497         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
17498         if(node && this.childrenRendered){
17499             node.render();
17500         }
17501         this.ui.updateExpandIcon();
17502         return node;
17503     },
17504
17505     // private override
17506     removeChild : function(node){
17507         this.ownerTree.getSelectionModel().unselect(node);
17508         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
17509         // if it's been rendered remove dom node
17510         if(this.childrenRendered){
17511             node.ui.remove();
17512         }
17513         if(this.childNodes.length < 1){
17514             this.collapse(false, false);
17515         }else{
17516             this.ui.updateExpandIcon();
17517         }
17518         if(!this.firstChild) {
17519             this.childrenRendered = false;
17520         }
17521         return node;
17522     },
17523
17524     // private override
17525     insertBefore : function(node, refNode){
17526         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
17527         if(newNode && refNode && this.childrenRendered){
17528             node.render();
17529         }
17530         this.ui.updateExpandIcon();
17531         return newNode;
17532     },
17533
17534     /**
17535      * Sets the text for this node
17536      * @param {String} text
17537      */
17538     setText : function(text){
17539         var oldText = this.text;
17540         this.text = text;
17541         this.attributes.text = text;
17542         if(this.rendered){ // event without subscribing
17543             this.ui.onTextChange(this, text, oldText);
17544         }
17545         this.fireEvent("textchange", this, text, oldText);
17546     },
17547
17548     /**
17549      * Triggers selection of this node
17550      */
17551     select : function(){
17552         this.getOwnerTree().getSelectionModel().select(this);
17553     },
17554
17555     /**
17556      * Triggers deselection of this node
17557      */
17558     unselect : function(){
17559         this.getOwnerTree().getSelectionModel().unselect(this);
17560     },
17561
17562     /**
17563      * Returns true if this node is selected
17564      * @return {Boolean}
17565      */
17566     isSelected : function(){
17567         return this.getOwnerTree().getSelectionModel().isSelected(this);
17568     },
17569
17570     /**
17571      * Expand this node.
17572      * @param {Boolean} deep (optional) True to expand all children as well
17573      * @param {Boolean} anim (optional) false to cancel the default animation
17574      * @param {Function} callback (optional) A callback to be called when
17575      * expanding this node completes (does not wait for deep expand to complete).
17576      * Called with 1 parameter, this node.
17577      */
17578     expand : function(deep, anim, callback){
17579         if(!this.expanded){
17580             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
17581                 return;
17582             }
17583             if(!this.childrenRendered){
17584                 this.renderChildren();
17585             }
17586             this.expanded = true;
17587             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17588                 this.ui.animExpand(function(){
17589                     this.fireEvent("expand", this);
17590                     if(typeof callback == "function"){
17591                         callback(this);
17592                     }
17593                     if(deep === true){
17594                         this.expandChildNodes(true);
17595                     }
17596                 }.createDelegate(this));
17597                 return;
17598             }else{
17599                 this.ui.expand();
17600                 this.fireEvent("expand", this);
17601                 if(typeof callback == "function"){
17602                     callback(this);
17603                 }
17604             }
17605         }else{
17606            if(typeof callback == "function"){
17607                callback(this);
17608            }
17609         }
17610         if(deep === true){
17611             this.expandChildNodes(true);
17612         }
17613     },
17614
17615     isHiddenRoot : function(){
17616         return this.isRoot && !this.getOwnerTree().rootVisible;
17617     },
17618
17619     /**
17620      * Collapse this node.
17621      * @param {Boolean} deep (optional) True to collapse all children as well
17622      * @param {Boolean} anim (optional) false to cancel the default animation
17623      */
17624     collapse : function(deep, anim){
17625         if(this.expanded && !this.isHiddenRoot()){
17626             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17627                 return;
17628             }
17629             this.expanded = false;
17630             if((this.getOwnerTree().animate && anim !== false) || anim){
17631                 this.ui.animCollapse(function(){
17632                     this.fireEvent("collapse", this);
17633                     if(deep === true){
17634                         this.collapseChildNodes(true);
17635                     }
17636                 }.createDelegate(this));
17637                 return;
17638             }else{
17639                 this.ui.collapse();
17640                 this.fireEvent("collapse", this);
17641             }
17642         }
17643         if(deep === true){
17644             var cs = this.childNodes;
17645             for(var i = 0, len = cs.length; i < len; i++) {
17646                 cs[i].collapse(true, false);
17647             }
17648         }
17649     },
17650
17651     // private
17652     delayedExpand : function(delay){
17653         if(!this.expandProcId){
17654             this.expandProcId = this.expand.defer(delay, this);
17655         }
17656     },
17657
17658     // private
17659     cancelExpand : function(){
17660         if(this.expandProcId){
17661             clearTimeout(this.expandProcId);
17662         }
17663         this.expandProcId = false;
17664     },
17665
17666     /**
17667      * Toggles expanded/collapsed state of the node
17668      */
17669     toggle : function(){
17670         if(this.expanded){
17671             this.collapse();
17672         }else{
17673             this.expand();
17674         }
17675     },
17676
17677     /**
17678      * Ensures all parent nodes are expanded
17679      */
17680     ensureVisible : function(callback){
17681         var tree = this.getOwnerTree();
17682         tree.expandPath(this.parentNode.getPath(), false, function(){
17683             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17684             Roo.callback(callback);
17685         }.createDelegate(this));
17686     },
17687
17688     /**
17689      * Expand all child nodes
17690      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17691      */
17692     expandChildNodes : function(deep){
17693         var cs = this.childNodes;
17694         for(var i = 0, len = cs.length; i < len; i++) {
17695                 cs[i].expand(deep);
17696         }
17697     },
17698
17699     /**
17700      * Collapse all child nodes
17701      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17702      */
17703     collapseChildNodes : function(deep){
17704         var cs = this.childNodes;
17705         for(var i = 0, len = cs.length; i < len; i++) {
17706                 cs[i].collapse(deep);
17707         }
17708     },
17709
17710     /**
17711      * Disables this node
17712      */
17713     disable : function(){
17714         this.disabled = true;
17715         this.unselect();
17716         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17717             this.ui.onDisableChange(this, true);
17718         }
17719         this.fireEvent("disabledchange", this, true);
17720     },
17721
17722     /**
17723      * Enables this node
17724      */
17725     enable : function(){
17726         this.disabled = false;
17727         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17728             this.ui.onDisableChange(this, false);
17729         }
17730         this.fireEvent("disabledchange", this, false);
17731     },
17732
17733     // private
17734     renderChildren : function(suppressEvent){
17735         if(suppressEvent !== false){
17736             this.fireEvent("beforechildrenrendered", this);
17737         }
17738         var cs = this.childNodes;
17739         for(var i = 0, len = cs.length; i < len; i++){
17740             cs[i].render(true);
17741         }
17742         this.childrenRendered = true;
17743     },
17744
17745     // private
17746     sort : function(fn, scope){
17747         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17748         if(this.childrenRendered){
17749             var cs = this.childNodes;
17750             for(var i = 0, len = cs.length; i < len; i++){
17751                 cs[i].render(true);
17752             }
17753         }
17754     },
17755
17756     // private
17757     render : function(bulkRender){
17758         this.ui.render(bulkRender);
17759         if(!this.rendered){
17760             this.rendered = true;
17761             if(this.expanded){
17762                 this.expanded = false;
17763                 this.expand(false, false);
17764             }
17765         }
17766     },
17767
17768     // private
17769     renderIndent : function(deep, refresh){
17770         if(refresh){
17771             this.ui.childIndent = null;
17772         }
17773         this.ui.renderIndent();
17774         if(deep === true && this.childrenRendered){
17775             var cs = this.childNodes;
17776             for(var i = 0, len = cs.length; i < len; i++){
17777                 cs[i].renderIndent(true, refresh);
17778             }
17779         }
17780     }
17781 });/*
17782  * Based on:
17783  * Ext JS Library 1.1.1
17784  * Copyright(c) 2006-2007, Ext JS, LLC.
17785  *
17786  * Originally Released Under LGPL - original licence link has changed is not relivant.
17787  *
17788  * Fork - LGPL
17789  * <script type="text/javascript">
17790  */
17791  
17792 /**
17793  * @class Roo.tree.AsyncTreeNode
17794  * @extends Roo.tree.TreeNode
17795  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17796  * @constructor
17797  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
17798  */
17799  Roo.tree.AsyncTreeNode = function(config){
17800     this.loaded = false;
17801     this.loading = false;
17802     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17803     /**
17804     * @event beforeload
17805     * Fires before this node is loaded, return false to cancel
17806     * @param {Node} this This node
17807     */
17808     this.addEvents({'beforeload':true, 'load': true});
17809     /**
17810     * @event load
17811     * Fires when this node is loaded
17812     * @param {Node} this This node
17813     */
17814     /**
17815      * The loader used by this node (defaults to using the tree's defined loader)
17816      * @type TreeLoader
17817      * @property loader
17818      */
17819 };
17820 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17821     expand : function(deep, anim, callback){
17822         if(this.loading){ // if an async load is already running, waiting til it's done
17823             var timer;
17824             var f = function(){
17825                 if(!this.loading){ // done loading
17826                     clearInterval(timer);
17827                     this.expand(deep, anim, callback);
17828                 }
17829             }.createDelegate(this);
17830             timer = setInterval(f, 200);
17831             return;
17832         }
17833         if(!this.loaded){
17834             if(this.fireEvent("beforeload", this) === false){
17835                 return;
17836             }
17837             this.loading = true;
17838             this.ui.beforeLoad(this);
17839             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17840             if(loader){
17841                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17842                 return;
17843             }
17844         }
17845         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17846     },
17847     
17848     /**
17849      * Returns true if this node is currently loading
17850      * @return {Boolean}
17851      */
17852     isLoading : function(){
17853         return this.loading;  
17854     },
17855     
17856     loadComplete : function(deep, anim, callback){
17857         this.loading = false;
17858         this.loaded = true;
17859         this.ui.afterLoad(this);
17860         this.fireEvent("load", this);
17861         this.expand(deep, anim, callback);
17862     },
17863     
17864     /**
17865      * Returns true if this node has been loaded
17866      * @return {Boolean}
17867      */
17868     isLoaded : function(){
17869         return this.loaded;
17870     },
17871     
17872     hasChildNodes : function(){
17873         if(!this.isLeaf() && !this.loaded){
17874             return true;
17875         }else{
17876             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17877         }
17878     },
17879
17880     /**
17881      * Trigger a reload for this node
17882      * @param {Function} callback
17883      */
17884     reload : function(callback){
17885         this.collapse(false, false);
17886         while(this.firstChild){
17887             this.removeChild(this.firstChild);
17888         }
17889         this.childrenRendered = false;
17890         this.loaded = false;
17891         if(this.isHiddenRoot()){
17892             this.expanded = false;
17893         }
17894         this.expand(false, false, callback);
17895     }
17896 });/*
17897  * Based on:
17898  * Ext JS Library 1.1.1
17899  * Copyright(c) 2006-2007, Ext JS, LLC.
17900  *
17901  * Originally Released Under LGPL - original licence link has changed is not relivant.
17902  *
17903  * Fork - LGPL
17904  * <script type="text/javascript">
17905  */
17906  
17907 /**
17908  * @class Roo.tree.TreeNodeUI
17909  * @constructor
17910  * @param {Object} node The node to render
17911  * The TreeNode UI implementation is separate from the
17912  * tree implementation. Unless you are customizing the tree UI,
17913  * you should never have to use this directly.
17914  */
17915 Roo.tree.TreeNodeUI = function(node){
17916     this.node = node;
17917     this.rendered = false;
17918     this.animating = false;
17919     this.emptyIcon = Roo.BLANK_IMAGE_URL;
17920 };
17921
17922 Roo.tree.TreeNodeUI.prototype = {
17923     removeChild : function(node){
17924         if(this.rendered){
17925             this.ctNode.removeChild(node.ui.getEl());
17926         }
17927     },
17928
17929     beforeLoad : function(){
17930          this.addClass("x-tree-node-loading");
17931     },
17932
17933     afterLoad : function(){
17934          this.removeClass("x-tree-node-loading");
17935     },
17936
17937     onTextChange : function(node, text, oldText){
17938         if(this.rendered){
17939             this.textNode.innerHTML = text;
17940         }
17941     },
17942
17943     onDisableChange : function(node, state){
17944         this.disabled = state;
17945         if(state){
17946             this.addClass("x-tree-node-disabled");
17947         }else{
17948             this.removeClass("x-tree-node-disabled");
17949         }
17950     },
17951
17952     onSelectedChange : function(state){
17953         if(state){
17954             this.focus();
17955             this.addClass("x-tree-selected");
17956         }else{
17957             //this.blur();
17958             this.removeClass("x-tree-selected");
17959         }
17960     },
17961
17962     onMove : function(tree, node, oldParent, newParent, index, refNode){
17963         this.childIndent = null;
17964         if(this.rendered){
17965             var targetNode = newParent.ui.getContainer();
17966             if(!targetNode){//target not rendered
17967                 this.holder = document.createElement("div");
17968                 this.holder.appendChild(this.wrap);
17969                 return;
17970             }
17971             var insertBefore = refNode ? refNode.ui.getEl() : null;
17972             if(insertBefore){
17973                 targetNode.insertBefore(this.wrap, insertBefore);
17974             }else{
17975                 targetNode.appendChild(this.wrap);
17976             }
17977             this.node.renderIndent(true);
17978         }
17979     },
17980
17981     addClass : function(cls){
17982         if(this.elNode){
17983             Roo.fly(this.elNode).addClass(cls);
17984         }
17985     },
17986
17987     removeClass : function(cls){
17988         if(this.elNode){
17989             Roo.fly(this.elNode).removeClass(cls);
17990         }
17991     },
17992
17993     remove : function(){
17994         if(this.rendered){
17995             this.holder = document.createElement("div");
17996             this.holder.appendChild(this.wrap);
17997         }
17998     },
17999
18000     fireEvent : function(){
18001         return this.node.fireEvent.apply(this.node, arguments);
18002     },
18003
18004     initEvents : function(){
18005         this.node.on("move", this.onMove, this);
18006         var E = Roo.EventManager;
18007         var a = this.anchor;
18008
18009         var el = Roo.fly(a, '_treeui');
18010
18011         if(Roo.isOpera){ // opera render bug ignores the CSS
18012             el.setStyle("text-decoration", "none");
18013         }
18014
18015         el.on("click", this.onClick, this);
18016         el.on("dblclick", this.onDblClick, this);
18017
18018         if(this.checkbox){
18019             Roo.EventManager.on(this.checkbox,
18020                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
18021         }
18022
18023         el.on("contextmenu", this.onContextMenu, this);
18024
18025         var icon = Roo.fly(this.iconNode);
18026         icon.on("click", this.onClick, this);
18027         icon.on("dblclick", this.onDblClick, this);
18028         icon.on("contextmenu", this.onContextMenu, this);
18029         E.on(this.ecNode, "click", this.ecClick, this, true);
18030
18031         if(this.node.disabled){
18032             this.addClass("x-tree-node-disabled");
18033         }
18034         if(this.node.hidden){
18035             this.addClass("x-tree-node-disabled");
18036         }
18037         var ot = this.node.getOwnerTree();
18038         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
18039         if(dd && (!this.node.isRoot || ot.rootVisible)){
18040             Roo.dd.Registry.register(this.elNode, {
18041                 node: this.node,
18042                 handles: this.getDDHandles(),
18043                 isHandle: false
18044             });
18045         }
18046     },
18047
18048     getDDHandles : function(){
18049         return [this.iconNode, this.textNode];
18050     },
18051
18052     hide : function(){
18053         if(this.rendered){
18054             this.wrap.style.display = "none";
18055         }
18056     },
18057
18058     show : function(){
18059         if(this.rendered){
18060             this.wrap.style.display = "";
18061         }
18062     },
18063
18064     onContextMenu : function(e){
18065         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
18066             e.preventDefault();
18067             this.focus();
18068             this.fireEvent("contextmenu", this.node, e);
18069         }
18070     },
18071
18072     onClick : function(e){
18073         if(this.dropping){
18074             e.stopEvent();
18075             return;
18076         }
18077         if(this.fireEvent("beforeclick", this.node, e) !== false){
18078             if(!this.disabled && this.node.attributes.href){
18079                 this.fireEvent("click", this.node, e);
18080                 return;
18081             }
18082             e.preventDefault();
18083             if(this.disabled){
18084                 return;
18085             }
18086
18087             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
18088                 this.node.toggle();
18089             }
18090
18091             this.fireEvent("click", this.node, e);
18092         }else{
18093             e.stopEvent();
18094         }
18095     },
18096
18097     onDblClick : function(e){
18098         e.preventDefault();
18099         if(this.disabled){
18100             return;
18101         }
18102         if(this.checkbox){
18103             this.toggleCheck();
18104         }
18105         if(!this.animating && this.node.hasChildNodes()){
18106             this.node.toggle();
18107         }
18108         this.fireEvent("dblclick", this.node, e);
18109     },
18110
18111     onCheckChange : function(){
18112         var checked = this.checkbox.checked;
18113         this.node.attributes.checked = checked;
18114         this.fireEvent('checkchange', this.node, checked);
18115     },
18116
18117     ecClick : function(e){
18118         if(!this.animating && this.node.hasChildNodes()){
18119             this.node.toggle();
18120         }
18121     },
18122
18123     startDrop : function(){
18124         this.dropping = true;
18125     },
18126
18127     // delayed drop so the click event doesn't get fired on a drop
18128     endDrop : function(){
18129        setTimeout(function(){
18130            this.dropping = false;
18131        }.createDelegate(this), 50);
18132     },
18133
18134     expand : function(){
18135         this.updateExpandIcon();
18136         this.ctNode.style.display = "";
18137     },
18138
18139     focus : function(){
18140         if(!this.node.preventHScroll){
18141             try{this.anchor.focus();
18142             }catch(e){}
18143         }else if(!Roo.isIE){
18144             try{
18145                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
18146                 var l = noscroll.scrollLeft;
18147                 this.anchor.focus();
18148                 noscroll.scrollLeft = l;
18149             }catch(e){}
18150         }
18151     },
18152
18153     toggleCheck : function(value){
18154         var cb = this.checkbox;
18155         if(cb){
18156             cb.checked = (value === undefined ? !cb.checked : value);
18157         }
18158     },
18159
18160     blur : function(){
18161         try{
18162             this.anchor.blur();
18163         }catch(e){}
18164     },
18165
18166     animExpand : function(callback){
18167         var ct = Roo.get(this.ctNode);
18168         ct.stopFx();
18169         if(!this.node.hasChildNodes()){
18170             this.updateExpandIcon();
18171             this.ctNode.style.display = "";
18172             Roo.callback(callback);
18173             return;
18174         }
18175         this.animating = true;
18176         this.updateExpandIcon();
18177
18178         ct.slideIn('t', {
18179            callback : function(){
18180                this.animating = false;
18181                Roo.callback(callback);
18182             },
18183             scope: this,
18184             duration: this.node.ownerTree.duration || .25
18185         });
18186     },
18187
18188     highlight : function(){
18189         var tree = this.node.getOwnerTree();
18190         Roo.fly(this.wrap).highlight(
18191             tree.hlColor || "C3DAF9",
18192             {endColor: tree.hlBaseColor}
18193         );
18194     },
18195
18196     collapse : function(){
18197         this.updateExpandIcon();
18198         this.ctNode.style.display = "none";
18199     },
18200
18201     animCollapse : function(callback){
18202         var ct = Roo.get(this.ctNode);
18203         ct.enableDisplayMode('block');
18204         ct.stopFx();
18205
18206         this.animating = true;
18207         this.updateExpandIcon();
18208
18209         ct.slideOut('t', {
18210             callback : function(){
18211                this.animating = false;
18212                Roo.callback(callback);
18213             },
18214             scope: this,
18215             duration: this.node.ownerTree.duration || .25
18216         });
18217     },
18218
18219     getContainer : function(){
18220         return this.ctNode;
18221     },
18222
18223     getEl : function(){
18224         return this.wrap;
18225     },
18226
18227     appendDDGhost : function(ghostNode){
18228         ghostNode.appendChild(this.elNode.cloneNode(true));
18229     },
18230
18231     getDDRepairXY : function(){
18232         return Roo.lib.Dom.getXY(this.iconNode);
18233     },
18234
18235     onRender : function(){
18236         this.render();
18237     },
18238
18239     render : function(bulkRender){
18240         var n = this.node, a = n.attributes;
18241         var targetNode = n.parentNode ?
18242               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
18243
18244         if(!this.rendered){
18245             this.rendered = true;
18246
18247             this.renderElements(n, a, targetNode, bulkRender);
18248
18249             if(a.qtip){
18250                if(this.textNode.setAttributeNS){
18251                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
18252                    if(a.qtipTitle){
18253                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
18254                    }
18255                }else{
18256                    this.textNode.setAttribute("ext:qtip", a.qtip);
18257                    if(a.qtipTitle){
18258                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
18259                    }
18260                }
18261             }else if(a.qtipCfg){
18262                 a.qtipCfg.target = Roo.id(this.textNode);
18263                 Roo.QuickTips.register(a.qtipCfg);
18264             }
18265             this.initEvents();
18266             if(!this.node.expanded){
18267                 this.updateExpandIcon();
18268             }
18269         }else{
18270             if(bulkRender === true) {
18271                 targetNode.appendChild(this.wrap);
18272             }
18273         }
18274     },
18275
18276     renderElements : function(n, a, targetNode, bulkRender)
18277     {
18278         // add some indent caching, this helps performance when rendering a large tree
18279         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18280         var t = n.getOwnerTree();
18281         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
18282         if (typeof(n.attributes.html) != 'undefined') {
18283             txt = n.attributes.html;
18284         }
18285         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
18286         var cb = typeof a.checked == 'boolean';
18287         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18288         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
18289             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
18290             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
18291             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
18292             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
18293             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
18294              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
18295                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
18296             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18297             "</li>"];
18298
18299         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18300             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18301                                 n.nextSibling.ui.getEl(), buf.join(""));
18302         }else{
18303             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18304         }
18305
18306         this.elNode = this.wrap.childNodes[0];
18307         this.ctNode = this.wrap.childNodes[1];
18308         var cs = this.elNode.childNodes;
18309         this.indentNode = cs[0];
18310         this.ecNode = cs[1];
18311         this.iconNode = cs[2];
18312         var index = 3;
18313         if(cb){
18314             this.checkbox = cs[3];
18315             index++;
18316         }
18317         this.anchor = cs[index];
18318         this.textNode = cs[index].firstChild;
18319     },
18320
18321     getAnchor : function(){
18322         return this.anchor;
18323     },
18324
18325     getTextEl : function(){
18326         return this.textNode;
18327     },
18328
18329     getIconEl : function(){
18330         return this.iconNode;
18331     },
18332
18333     isChecked : function(){
18334         return this.checkbox ? this.checkbox.checked : false;
18335     },
18336
18337     updateExpandIcon : function(){
18338         if(this.rendered){
18339             var n = this.node, c1, c2;
18340             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
18341             var hasChild = n.hasChildNodes();
18342             if(hasChild){
18343                 if(n.expanded){
18344                     cls += "-minus";
18345                     c1 = "x-tree-node-collapsed";
18346                     c2 = "x-tree-node-expanded";
18347                 }else{
18348                     cls += "-plus";
18349                     c1 = "x-tree-node-expanded";
18350                     c2 = "x-tree-node-collapsed";
18351                 }
18352                 if(this.wasLeaf){
18353                     this.removeClass("x-tree-node-leaf");
18354                     this.wasLeaf = false;
18355                 }
18356                 if(this.c1 != c1 || this.c2 != c2){
18357                     Roo.fly(this.elNode).replaceClass(c1, c2);
18358                     this.c1 = c1; this.c2 = c2;
18359                 }
18360             }else{
18361                 // this changes non-leafs into leafs if they have no children.
18362                 // it's not very rational behaviour..
18363                 
18364                 if(!this.wasLeaf && this.node.leaf){
18365                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
18366                     delete this.c1;
18367                     delete this.c2;
18368                     this.wasLeaf = true;
18369                 }
18370             }
18371             var ecc = "x-tree-ec-icon "+cls;
18372             if(this.ecc != ecc){
18373                 this.ecNode.className = ecc;
18374                 this.ecc = ecc;
18375             }
18376         }
18377     },
18378
18379     getChildIndent : function(){
18380         if(!this.childIndent){
18381             var buf = [];
18382             var p = this.node;
18383             while(p){
18384                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
18385                     if(!p.isLast()) {
18386                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
18387                     } else {
18388                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
18389                     }
18390                 }
18391                 p = p.parentNode;
18392             }
18393             this.childIndent = buf.join("");
18394         }
18395         return this.childIndent;
18396     },
18397
18398     renderIndent : function(){
18399         if(this.rendered){
18400             var indent = "";
18401             var p = this.node.parentNode;
18402             if(p){
18403                 indent = p.ui.getChildIndent();
18404             }
18405             if(this.indentMarkup != indent){ // don't rerender if not required
18406                 this.indentNode.innerHTML = indent;
18407                 this.indentMarkup = indent;
18408             }
18409             this.updateExpandIcon();
18410         }
18411     }
18412 };
18413
18414 Roo.tree.RootTreeNodeUI = function(){
18415     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
18416 };
18417 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
18418     render : function(){
18419         if(!this.rendered){
18420             var targetNode = this.node.ownerTree.innerCt.dom;
18421             this.node.expanded = true;
18422             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
18423             this.wrap = this.ctNode = targetNode.firstChild;
18424         }
18425     },
18426     collapse : function(){
18427     },
18428     expand : function(){
18429     }
18430 });/*
18431  * Based on:
18432  * Ext JS Library 1.1.1
18433  * Copyright(c) 2006-2007, Ext JS, LLC.
18434  *
18435  * Originally Released Under LGPL - original licence link has changed is not relivant.
18436  *
18437  * Fork - LGPL
18438  * <script type="text/javascript">
18439  */
18440 /**
18441  * @class Roo.tree.TreeLoader
18442  * @extends Roo.util.Observable
18443  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
18444  * nodes from a specified URL. The response must be a javascript Array definition
18445  * who's elements are node definition objects. eg:
18446  * <pre><code>
18447 {  success : true,
18448    data :      [
18449    
18450     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
18451     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
18452     ]
18453 }
18454
18455
18456 </code></pre>
18457  * <br><br>
18458  * The old style respose with just an array is still supported, but not recommended.
18459  * <br><br>
18460  *
18461  * A server request is sent, and child nodes are loaded only when a node is expanded.
18462  * The loading node's id is passed to the server under the parameter name "node" to
18463  * enable the server to produce the correct child nodes.
18464  * <br><br>
18465  * To pass extra parameters, an event handler may be attached to the "beforeload"
18466  * event, and the parameters specified in the TreeLoader's baseParams property:
18467  * <pre><code>
18468     myTreeLoader.on("beforeload", function(treeLoader, node) {
18469         this.baseParams.category = node.attributes.category;
18470     }, this);
18471 </code></pre><
18472  * This would pass an HTTP parameter called "category" to the server containing
18473  * the value of the Node's "category" attribute.
18474  * @constructor
18475  * Creates a new Treeloader.
18476  * @param {Object} config A config object containing config properties.
18477  */
18478 Roo.tree.TreeLoader = function(config){
18479     this.baseParams = {};
18480     this.requestMethod = "POST";
18481     Roo.apply(this, config);
18482
18483     this.addEvents({
18484     
18485         /**
18486          * @event beforeload
18487          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
18488          * @param {Object} This TreeLoader object.
18489          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18490          * @param {Object} callback The callback function specified in the {@link #load} call.
18491          */
18492         beforeload : true,
18493         /**
18494          * @event load
18495          * Fires when the node has been successfuly loaded.
18496          * @param {Object} This TreeLoader object.
18497          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18498          * @param {Object} response The response object containing the data from the server.
18499          */
18500         load : true,
18501         /**
18502          * @event loadexception
18503          * Fires if the network request failed.
18504          * @param {Object} This TreeLoader object.
18505          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18506          * @param {Object} response The response object containing the data from the server.
18507          */
18508         loadexception : true,
18509         /**
18510          * @event create
18511          * Fires before a node is created, enabling you to return custom Node types 
18512          * @param {Object} This TreeLoader object.
18513          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
18514          */
18515         create : true
18516     });
18517
18518     Roo.tree.TreeLoader.superclass.constructor.call(this);
18519 };
18520
18521 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
18522     /**
18523     * @cfg {String} dataUrl The URL from which to request a Json string which
18524     * specifies an array of node definition object representing the child nodes
18525     * to be loaded.
18526     */
18527     /**
18528     * @cfg {String} requestMethod either GET or POST
18529     * defaults to POST (due to BC)
18530     * to be loaded.
18531     */
18532     /**
18533     * @cfg {Object} baseParams (optional) An object containing properties which
18534     * specify HTTP parameters to be passed to each request for child nodes.
18535     */
18536     /**
18537     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
18538     * created by this loader. If the attributes sent by the server have an attribute in this object,
18539     * they take priority.
18540     */
18541     /**
18542     * @cfg {Object} uiProviders (optional) An object containing properties which
18543     * 
18544     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
18545     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
18546     * <i>uiProvider</i> attribute of a returned child node is a string rather
18547     * than a reference to a TreeNodeUI implementation, this that string value
18548     * is used as a property name in the uiProviders object. You can define the provider named
18549     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
18550     */
18551     uiProviders : {},
18552
18553     /**
18554     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
18555     * child nodes before loading.
18556     */
18557     clearOnLoad : true,
18558
18559     /**
18560     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
18561     * property on loading, rather than expecting an array. (eg. more compatible to a standard
18562     * Grid query { data : [ .....] }
18563     */
18564     
18565     root : false,
18566      /**
18567     * @cfg {String} queryParam (optional) 
18568     * Name of the query as it will be passed on the querystring (defaults to 'node')
18569     * eg. the request will be ?node=[id]
18570     */
18571     
18572     
18573     queryParam: false,
18574     
18575     /**
18576      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
18577      * This is called automatically when a node is expanded, but may be used to reload
18578      * a node (or append new children if the {@link #clearOnLoad} option is false.)
18579      * @param {Roo.tree.TreeNode} node
18580      * @param {Function} callback
18581      */
18582     load : function(node, callback){
18583         if(this.clearOnLoad){
18584             while(node.firstChild){
18585                 node.removeChild(node.firstChild);
18586             }
18587         }
18588         if(node.attributes.children){ // preloaded json children
18589             var cs = node.attributes.children;
18590             for(var i = 0, len = cs.length; i < len; i++){
18591                 node.appendChild(this.createNode(cs[i]));
18592             }
18593             if(typeof callback == "function"){
18594                 callback();
18595             }
18596         }else if(this.dataUrl){
18597             this.requestData(node, callback);
18598         }
18599     },
18600
18601     getParams: function(node){
18602         var buf = [], bp = this.baseParams;
18603         for(var key in bp){
18604             if(typeof bp[key] != "function"){
18605                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18606             }
18607         }
18608         var n = this.queryParam === false ? 'node' : this.queryParam;
18609         buf.push(n + "=", encodeURIComponent(node.id));
18610         return buf.join("");
18611     },
18612
18613     requestData : function(node, callback){
18614         if(this.fireEvent("beforeload", this, node, callback) !== false){
18615             this.transId = Roo.Ajax.request({
18616                 method:this.requestMethod,
18617                 url: this.dataUrl||this.url,
18618                 success: this.handleResponse,
18619                 failure: this.handleFailure,
18620                 scope: this,
18621                 argument: {callback: callback, node: node},
18622                 params: this.getParams(node)
18623             });
18624         }else{
18625             // if the load is cancelled, make sure we notify
18626             // the node that we are done
18627             if(typeof callback == "function"){
18628                 callback();
18629             }
18630         }
18631     },
18632
18633     isLoading : function(){
18634         return this.transId ? true : false;
18635     },
18636
18637     abort : function(){
18638         if(this.isLoading()){
18639             Roo.Ajax.abort(this.transId);
18640         }
18641     },
18642
18643     // private
18644     createNode : function(attr)
18645     {
18646         // apply baseAttrs, nice idea Corey!
18647         if(this.baseAttrs){
18648             Roo.applyIf(attr, this.baseAttrs);
18649         }
18650         if(this.applyLoader !== false){
18651             attr.loader = this;
18652         }
18653         // uiProvider = depreciated..
18654         
18655         if(typeof(attr.uiProvider) == 'string'){
18656            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
18657                 /**  eval:var:attr */ eval(attr.uiProvider);
18658         }
18659         if(typeof(this.uiProviders['default']) != 'undefined') {
18660             attr.uiProvider = this.uiProviders['default'];
18661         }
18662         
18663         this.fireEvent('create', this, attr);
18664         
18665         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18666         return(attr.leaf ?
18667                         new Roo.tree.TreeNode(attr) :
18668                         new Roo.tree.AsyncTreeNode(attr));
18669     },
18670
18671     processResponse : function(response, node, callback)
18672     {
18673         var json = response.responseText;
18674         try {
18675             
18676             var o = Roo.decode(json);
18677             
18678             if (this.root === false && typeof(o.success) != undefined) {
18679                 this.root = 'data'; // the default behaviour for list like data..
18680                 }
18681                 
18682             if (this.root !== false &&  !o.success) {
18683                 // it's a failure condition.
18684                 var a = response.argument;
18685                 this.fireEvent("loadexception", this, a.node, response);
18686                 Roo.log("Load failed - should have a handler really");
18687                 return;
18688             }
18689             
18690             
18691             
18692             if (this.root !== false) {
18693                  o = o[this.root];
18694             }
18695             
18696             for(var i = 0, len = o.length; i < len; i++){
18697                 var n = this.createNode(o[i]);
18698                 if(n){
18699                     node.appendChild(n);
18700                 }
18701             }
18702             if(typeof callback == "function"){
18703                 callback(this, node);
18704             }
18705         }catch(e){
18706             this.handleFailure(response);
18707         }
18708     },
18709
18710     handleResponse : function(response){
18711         this.transId = false;
18712         var a = response.argument;
18713         this.processResponse(response, a.node, a.callback);
18714         this.fireEvent("load", this, a.node, response);
18715     },
18716
18717     handleFailure : function(response)
18718     {
18719         // should handle failure better..
18720         this.transId = false;
18721         var a = response.argument;
18722         this.fireEvent("loadexception", this, a.node, response);
18723         if(typeof a.callback == "function"){
18724             a.callback(this, a.node);
18725         }
18726     }
18727 });/*
18728  * Based on:
18729  * Ext JS Library 1.1.1
18730  * Copyright(c) 2006-2007, Ext JS, LLC.
18731  *
18732  * Originally Released Under LGPL - original licence link has changed is not relivant.
18733  *
18734  * Fork - LGPL
18735  * <script type="text/javascript">
18736  */
18737
18738 /**
18739 * @class Roo.tree.TreeFilter
18740 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18741 * @param {TreePanel} tree
18742 * @param {Object} config (optional)
18743  */
18744 Roo.tree.TreeFilter = function(tree, config){
18745     this.tree = tree;
18746     this.filtered = {};
18747     Roo.apply(this, config);
18748 };
18749
18750 Roo.tree.TreeFilter.prototype = {
18751     clearBlank:false,
18752     reverse:false,
18753     autoClear:false,
18754     remove:false,
18755
18756      /**
18757      * Filter the data by a specific attribute.
18758      * @param {String/RegExp} value Either string that the attribute value
18759      * should start with or a RegExp to test against the attribute
18760      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18761      * @param {TreeNode} startNode (optional) The node to start the filter at.
18762      */
18763     filter : function(value, attr, startNode){
18764         attr = attr || "text";
18765         var f;
18766         if(typeof value == "string"){
18767             var vlen = value.length;
18768             // auto clear empty filter
18769             if(vlen == 0 && this.clearBlank){
18770                 this.clear();
18771                 return;
18772             }
18773             value = value.toLowerCase();
18774             f = function(n){
18775                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18776             };
18777         }else if(value.exec){ // regex?
18778             f = function(n){
18779                 return value.test(n.attributes[attr]);
18780             };
18781         }else{
18782             throw 'Illegal filter type, must be string or regex';
18783         }
18784         this.filterBy(f, null, startNode);
18785         },
18786
18787     /**
18788      * Filter by a function. The passed function will be called with each
18789      * node in the tree (or from the startNode). If the function returns true, the node is kept
18790      * otherwise it is filtered. If a node is filtered, its children are also filtered.
18791      * @param {Function} fn The filter function
18792      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18793      */
18794     filterBy : function(fn, scope, startNode){
18795         startNode = startNode || this.tree.root;
18796         if(this.autoClear){
18797             this.clear();
18798         }
18799         var af = this.filtered, rv = this.reverse;
18800         var f = function(n){
18801             if(n == startNode){
18802                 return true;
18803             }
18804             if(af[n.id]){
18805                 return false;
18806             }
18807             var m = fn.call(scope || n, n);
18808             if(!m || rv){
18809                 af[n.id] = n;
18810                 n.ui.hide();
18811                 return false;
18812             }
18813             return true;
18814         };
18815         startNode.cascade(f);
18816         if(this.remove){
18817            for(var id in af){
18818                if(typeof id != "function"){
18819                    var n = af[id];
18820                    if(n && n.parentNode){
18821                        n.parentNode.removeChild(n);
18822                    }
18823                }
18824            }
18825         }
18826     },
18827
18828     /**
18829      * Clears the current filter. Note: with the "remove" option
18830      * set a filter cannot be cleared.
18831      */
18832     clear : function(){
18833         var t = this.tree;
18834         var af = this.filtered;
18835         for(var id in af){
18836             if(typeof id != "function"){
18837                 var n = af[id];
18838                 if(n){
18839                     n.ui.show();
18840                 }
18841             }
18842         }
18843         this.filtered = {};
18844     }
18845 };
18846 /*
18847  * Based on:
18848  * Ext JS Library 1.1.1
18849  * Copyright(c) 2006-2007, Ext JS, LLC.
18850  *
18851  * Originally Released Under LGPL - original licence link has changed is not relivant.
18852  *
18853  * Fork - LGPL
18854  * <script type="text/javascript">
18855  */
18856  
18857
18858 /**
18859  * @class Roo.tree.TreeSorter
18860  * Provides sorting of nodes in a TreePanel
18861  * 
18862  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18863  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18864  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18865  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18866  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18867  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18868  * @constructor
18869  * @param {TreePanel} tree
18870  * @param {Object} config
18871  */
18872 Roo.tree.TreeSorter = function(tree, config){
18873     Roo.apply(this, config);
18874     tree.on("beforechildrenrendered", this.doSort, this);
18875     tree.on("append", this.updateSort, this);
18876     tree.on("insert", this.updateSort, this);
18877     
18878     var dsc = this.dir && this.dir.toLowerCase() == "desc";
18879     var p = this.property || "text";
18880     var sortType = this.sortType;
18881     var fs = this.folderSort;
18882     var cs = this.caseSensitive === true;
18883     var leafAttr = this.leafAttr || 'leaf';
18884
18885     this.sortFn = function(n1, n2){
18886         if(fs){
18887             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18888                 return 1;
18889             }
18890             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
18891                 return -1;
18892             }
18893         }
18894         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
18895         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
18896         if(v1 < v2){
18897                         return dsc ? +1 : -1;
18898                 }else if(v1 > v2){
18899                         return dsc ? -1 : +1;
18900         }else{
18901                 return 0;
18902         }
18903     };
18904 };
18905
18906 Roo.tree.TreeSorter.prototype = {
18907     doSort : function(node){
18908         node.sort(this.sortFn);
18909     },
18910     
18911     compareNodes : function(n1, n2){
18912         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
18913     },
18914     
18915     updateSort : function(tree, node){
18916         if(node.childrenRendered){
18917             this.doSort.defer(1, this, [node]);
18918         }
18919     }
18920 };/*
18921  * Based on:
18922  * Ext JS Library 1.1.1
18923  * Copyright(c) 2006-2007, Ext JS, LLC.
18924  *
18925  * Originally Released Under LGPL - original licence link has changed is not relivant.
18926  *
18927  * Fork - LGPL
18928  * <script type="text/javascript">
18929  */
18930
18931 if(Roo.dd.DropZone){
18932     
18933 Roo.tree.TreeDropZone = function(tree, config){
18934     this.allowParentInsert = false;
18935     this.allowContainerDrop = false;
18936     this.appendOnly = false;
18937     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
18938     this.tree = tree;
18939     this.lastInsertClass = "x-tree-no-status";
18940     this.dragOverData = {};
18941 };
18942
18943 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
18944     ddGroup : "TreeDD",
18945     scroll:  true,
18946     
18947     expandDelay : 1000,
18948     
18949     expandNode : function(node){
18950         if(node.hasChildNodes() && !node.isExpanded()){
18951             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
18952         }
18953     },
18954     
18955     queueExpand : function(node){
18956         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
18957     },
18958     
18959     cancelExpand : function(){
18960         if(this.expandProcId){
18961             clearTimeout(this.expandProcId);
18962             this.expandProcId = false;
18963         }
18964     },
18965     
18966     isValidDropPoint : function(n, pt, dd, e, data){
18967         if(!n || !data){ return false; }
18968         var targetNode = n.node;
18969         var dropNode = data.node;
18970         // default drop rules
18971         if(!(targetNode && targetNode.isTarget && pt)){
18972             return false;
18973         }
18974         if(pt == "append" && targetNode.allowChildren === false){
18975             return false;
18976         }
18977         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
18978             return false;
18979         }
18980         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
18981             return false;
18982         }
18983         // reuse the object
18984         var overEvent = this.dragOverData;
18985         overEvent.tree = this.tree;
18986         overEvent.target = targetNode;
18987         overEvent.data = data;
18988         overEvent.point = pt;
18989         overEvent.source = dd;
18990         overEvent.rawEvent = e;
18991         overEvent.dropNode = dropNode;
18992         overEvent.cancel = false;  
18993         var result = this.tree.fireEvent("nodedragover", overEvent);
18994         return overEvent.cancel === false && result !== false;
18995     },
18996     
18997     getDropPoint : function(e, n, dd)
18998     {
18999         var tn = n.node;
19000         if(tn.isRoot){
19001             return tn.allowChildren !== false ? "append" : false; // always append for root
19002         }
19003         var dragEl = n.ddel;
19004         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
19005         var y = Roo.lib.Event.getPageY(e);
19006         //var noAppend = tn.allowChildren === false || tn.isLeaf();
19007         
19008         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
19009         var noAppend = tn.allowChildren === false;
19010         if(this.appendOnly || tn.parentNode.allowChildren === false){
19011             return noAppend ? false : "append";
19012         }
19013         var noBelow = false;
19014         if(!this.allowParentInsert){
19015             noBelow = tn.hasChildNodes() && tn.isExpanded();
19016         }
19017         var q = (b - t) / (noAppend ? 2 : 3);
19018         if(y >= t && y < (t + q)){
19019             return "above";
19020         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
19021             return "below";
19022         }else{
19023             return "append";
19024         }
19025     },
19026     
19027     onNodeEnter : function(n, dd, e, data)
19028     {
19029         this.cancelExpand();
19030     },
19031     
19032     onNodeOver : function(n, dd, e, data)
19033     {
19034        
19035         var pt = this.getDropPoint(e, n, dd);
19036         var node = n.node;
19037         
19038         // auto node expand check
19039         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
19040             this.queueExpand(node);
19041         }else if(pt != "append"){
19042             this.cancelExpand();
19043         }
19044         
19045         // set the insert point style on the target node
19046         var returnCls = this.dropNotAllowed;
19047         if(this.isValidDropPoint(n, pt, dd, e, data)){
19048            if(pt){
19049                var el = n.ddel;
19050                var cls;
19051                if(pt == "above"){
19052                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
19053                    cls = "x-tree-drag-insert-above";
19054                }else if(pt == "below"){
19055                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
19056                    cls = "x-tree-drag-insert-below";
19057                }else{
19058                    returnCls = "x-tree-drop-ok-append";
19059                    cls = "x-tree-drag-append";
19060                }
19061                if(this.lastInsertClass != cls){
19062                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
19063                    this.lastInsertClass = cls;
19064                }
19065            }
19066        }
19067        return returnCls;
19068     },
19069     
19070     onNodeOut : function(n, dd, e, data){
19071         
19072         this.cancelExpand();
19073         this.removeDropIndicators(n);
19074     },
19075     
19076     onNodeDrop : function(n, dd, e, data){
19077         var point = this.getDropPoint(e, n, dd);
19078         var targetNode = n.node;
19079         targetNode.ui.startDrop();
19080         if(!this.isValidDropPoint(n, point, dd, e, data)){
19081             targetNode.ui.endDrop();
19082             return false;
19083         }
19084         // first try to find the drop node
19085         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
19086         var dropEvent = {
19087             tree : this.tree,
19088             target: targetNode,
19089             data: data,
19090             point: point,
19091             source: dd,
19092             rawEvent: e,
19093             dropNode: dropNode,
19094             cancel: !dropNode   
19095         };
19096         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
19097         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
19098             targetNode.ui.endDrop();
19099             return false;
19100         }
19101         // allow target changing
19102         targetNode = dropEvent.target;
19103         if(point == "append" && !targetNode.isExpanded()){
19104             targetNode.expand(false, null, function(){
19105                 this.completeDrop(dropEvent);
19106             }.createDelegate(this));
19107         }else{
19108             this.completeDrop(dropEvent);
19109         }
19110         return true;
19111     },
19112     
19113     completeDrop : function(de){
19114         var ns = de.dropNode, p = de.point, t = de.target;
19115         if(!(ns instanceof Array)){
19116             ns = [ns];
19117         }
19118         var n;
19119         for(var i = 0, len = ns.length; i < len; i++){
19120             n = ns[i];
19121             if(p == "above"){
19122                 t.parentNode.insertBefore(n, t);
19123             }else if(p == "below"){
19124                 t.parentNode.insertBefore(n, t.nextSibling);
19125             }else{
19126                 t.appendChild(n);
19127             }
19128         }
19129         n.ui.focus();
19130         if(this.tree.hlDrop){
19131             n.ui.highlight();
19132         }
19133         t.ui.endDrop();
19134         this.tree.fireEvent("nodedrop", de);
19135     },
19136     
19137     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
19138         if(this.tree.hlDrop){
19139             dropNode.ui.focus();
19140             dropNode.ui.highlight();
19141         }
19142         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
19143     },
19144     
19145     getTree : function(){
19146         return this.tree;
19147     },
19148     
19149     removeDropIndicators : function(n){
19150         if(n && n.ddel){
19151             var el = n.ddel;
19152             Roo.fly(el).removeClass([
19153                     "x-tree-drag-insert-above",
19154                     "x-tree-drag-insert-below",
19155                     "x-tree-drag-append"]);
19156             this.lastInsertClass = "_noclass";
19157         }
19158     },
19159     
19160     beforeDragDrop : function(target, e, id){
19161         this.cancelExpand();
19162         return true;
19163     },
19164     
19165     afterRepair : function(data){
19166         if(data && Roo.enableFx){
19167             data.node.ui.highlight();
19168         }
19169         this.hideProxy();
19170     } 
19171     
19172 });
19173
19174 }
19175 /*
19176  * Based on:
19177  * Ext JS Library 1.1.1
19178  * Copyright(c) 2006-2007, Ext JS, LLC.
19179  *
19180  * Originally Released Under LGPL - original licence link has changed is not relivant.
19181  *
19182  * Fork - LGPL
19183  * <script type="text/javascript">
19184  */
19185  
19186
19187 if(Roo.dd.DragZone){
19188 Roo.tree.TreeDragZone = function(tree, config){
19189     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
19190     this.tree = tree;
19191 };
19192
19193 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
19194     ddGroup : "TreeDD",
19195    
19196     onBeforeDrag : function(data, e){
19197         var n = data.node;
19198         return n && n.draggable && !n.disabled;
19199     },
19200      
19201     
19202     onInitDrag : function(e){
19203         var data = this.dragData;
19204         this.tree.getSelectionModel().select(data.node);
19205         this.proxy.update("");
19206         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
19207         this.tree.fireEvent("startdrag", this.tree, data.node, e);
19208     },
19209     
19210     getRepairXY : function(e, data){
19211         return data.node.ui.getDDRepairXY();
19212     },
19213     
19214     onEndDrag : function(data, e){
19215         this.tree.fireEvent("enddrag", this.tree, data.node, e);
19216         
19217         
19218     },
19219     
19220     onValidDrop : function(dd, e, id){
19221         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
19222         this.hideProxy();
19223     },
19224     
19225     beforeInvalidDrop : function(e, id){
19226         // this scrolls the original position back into view
19227         var sm = this.tree.getSelectionModel();
19228         sm.clearSelections();
19229         sm.select(this.dragData.node);
19230     }
19231 });
19232 }/*
19233  * Based on:
19234  * Ext JS Library 1.1.1
19235  * Copyright(c) 2006-2007, Ext JS, LLC.
19236  *
19237  * Originally Released Under LGPL - original licence link has changed is not relivant.
19238  *
19239  * Fork - LGPL
19240  * <script type="text/javascript">
19241  */
19242 /**
19243  * @class Roo.tree.TreeEditor
19244  * @extends Roo.Editor
19245  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
19246  * as the editor field.
19247  * @constructor
19248  * @param {Object} config (used to be the tree panel.)
19249  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
19250  * 
19251  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
19252  * @cfg {Roo.form.TextField|Object} field The field configuration
19253  *
19254  * 
19255  */
19256 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
19257     var tree = config;
19258     var field;
19259     if (oldconfig) { // old style..
19260         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
19261     } else {
19262         // new style..
19263         tree = config.tree;
19264         config.field = config.field  || {};
19265         config.field.xtype = 'TextField';
19266         field = Roo.factory(config.field, Roo.form);
19267     }
19268     config = config || {};
19269     
19270     
19271     this.addEvents({
19272         /**
19273          * @event beforenodeedit
19274          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
19275          * false from the handler of this event.
19276          * @param {Editor} this
19277          * @param {Roo.tree.Node} node 
19278          */
19279         "beforenodeedit" : true
19280     });
19281     
19282     //Roo.log(config);
19283     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
19284
19285     this.tree = tree;
19286
19287     tree.on('beforeclick', this.beforeNodeClick, this);
19288     tree.getTreeEl().on('mousedown', this.hide, this);
19289     this.on('complete', this.updateNode, this);
19290     this.on('beforestartedit', this.fitToTree, this);
19291     this.on('startedit', this.bindScroll, this, {delay:10});
19292     this.on('specialkey', this.onSpecialKey, this);
19293 };
19294
19295 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
19296     /**
19297      * @cfg {String} alignment
19298      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
19299      */
19300     alignment: "l-l",
19301     // inherit
19302     autoSize: false,
19303     /**
19304      * @cfg {Boolean} hideEl
19305      * True to hide the bound element while the editor is displayed (defaults to false)
19306      */
19307     hideEl : false,
19308     /**
19309      * @cfg {String} cls
19310      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
19311      */
19312     cls: "x-small-editor x-tree-editor",
19313     /**
19314      * @cfg {Boolean} shim
19315      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
19316      */
19317     shim:false,
19318     // inherit
19319     shadow:"frame",
19320     /**
19321      * @cfg {Number} maxWidth
19322      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
19323      * the containing tree element's size, it will be automatically limited for you to the container width, taking
19324      * scroll and client offsets into account prior to each edit.
19325      */
19326     maxWidth: 250,
19327
19328     editDelay : 350,
19329
19330     // private
19331     fitToTree : function(ed, el){
19332         var td = this.tree.getTreeEl().dom, nd = el.dom;
19333         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
19334             td.scrollLeft = nd.offsetLeft;
19335         }
19336         var w = Math.min(
19337                 this.maxWidth,
19338                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
19339         this.setSize(w, '');
19340         
19341         return this.fireEvent('beforenodeedit', this, this.editNode);
19342         
19343     },
19344
19345     // private
19346     triggerEdit : function(node){
19347         this.completeEdit();
19348         this.editNode = node;
19349         this.startEdit(node.ui.textNode, node.text);
19350     },
19351
19352     // private
19353     bindScroll : function(){
19354         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
19355     },
19356
19357     // private
19358     beforeNodeClick : function(node, e){
19359         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
19360         this.lastClick = new Date();
19361         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
19362             e.stopEvent();
19363             this.triggerEdit(node);
19364             return false;
19365         }
19366         return true;
19367     },
19368
19369     // private
19370     updateNode : function(ed, value){
19371         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
19372         this.editNode.setText(value);
19373     },
19374
19375     // private
19376     onHide : function(){
19377         Roo.tree.TreeEditor.superclass.onHide.call(this);
19378         if(this.editNode){
19379             this.editNode.ui.focus();
19380         }
19381     },
19382
19383     // private
19384     onSpecialKey : function(field, e){
19385         var k = e.getKey();
19386         if(k == e.ESC){
19387             e.stopEvent();
19388             this.cancelEdit();
19389         }else if(k == e.ENTER && !e.hasModifier()){
19390             e.stopEvent();
19391             this.completeEdit();
19392         }
19393     }
19394 });//<Script type="text/javascript">
19395 /*
19396  * Based on:
19397  * Ext JS Library 1.1.1
19398  * Copyright(c) 2006-2007, Ext JS, LLC.
19399  *
19400  * Originally Released Under LGPL - original licence link has changed is not relivant.
19401  *
19402  * Fork - LGPL
19403  * <script type="text/javascript">
19404  */
19405  
19406 /**
19407  * Not documented??? - probably should be...
19408  */
19409
19410 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
19411     //focus: Roo.emptyFn, // prevent odd scrolling behavior
19412     
19413     renderElements : function(n, a, targetNode, bulkRender){
19414         //consel.log("renderElements?");
19415         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
19416
19417         var t = n.getOwnerTree();
19418         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
19419         
19420         var cols = t.columns;
19421         var bw = t.borderWidth;
19422         var c = cols[0];
19423         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
19424          var cb = typeof a.checked == "boolean";
19425         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19426         var colcls = 'x-t-' + tid + '-c0';
19427         var buf = [
19428             '<li class="x-tree-node">',
19429             
19430                 
19431                 '<div class="x-tree-node-el ', a.cls,'">',
19432                     // extran...
19433                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
19434                 
19435                 
19436                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
19437                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
19438                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
19439                            (a.icon ? ' x-tree-node-inline-icon' : ''),
19440                            (a.iconCls ? ' '+a.iconCls : ''),
19441                            '" unselectable="on" />',
19442                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
19443                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
19444                              
19445                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19446                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
19447                             '<span unselectable="on" qtip="' + tx + '">',
19448                              tx,
19449                              '</span></a>' ,
19450                     '</div>',
19451                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19452                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
19453                  ];
19454         for(var i = 1, len = cols.length; i < len; i++){
19455             c = cols[i];
19456             colcls = 'x-t-' + tid + '-c' +i;
19457             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19458             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
19459                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
19460                       "</div>");
19461          }
19462          
19463          buf.push(
19464             '</a>',
19465             '<div class="x-clear"></div></div>',
19466             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
19467             "</li>");
19468         
19469         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
19470             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
19471                                 n.nextSibling.ui.getEl(), buf.join(""));
19472         }else{
19473             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
19474         }
19475         var el = this.wrap.firstChild;
19476         this.elRow = el;
19477         this.elNode = el.firstChild;
19478         this.ranchor = el.childNodes[1];
19479         this.ctNode = this.wrap.childNodes[1];
19480         var cs = el.firstChild.childNodes;
19481         this.indentNode = cs[0];
19482         this.ecNode = cs[1];
19483         this.iconNode = cs[2];
19484         var index = 3;
19485         if(cb){
19486             this.checkbox = cs[3];
19487             index++;
19488         }
19489         this.anchor = cs[index];
19490         
19491         this.textNode = cs[index].firstChild;
19492         
19493         //el.on("click", this.onClick, this);
19494         //el.on("dblclick", this.onDblClick, this);
19495         
19496         
19497        // console.log(this);
19498     },
19499     initEvents : function(){
19500         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
19501         
19502             
19503         var a = this.ranchor;
19504
19505         var el = Roo.get(a);
19506
19507         if(Roo.isOpera){ // opera render bug ignores the CSS
19508             el.setStyle("text-decoration", "none");
19509         }
19510
19511         el.on("click", this.onClick, this);
19512         el.on("dblclick", this.onDblClick, this);
19513         el.on("contextmenu", this.onContextMenu, this);
19514         
19515     },
19516     
19517     /*onSelectedChange : function(state){
19518         if(state){
19519             this.focus();
19520             this.addClass("x-tree-selected");
19521         }else{
19522             //this.blur();
19523             this.removeClass("x-tree-selected");
19524         }
19525     },*/
19526     addClass : function(cls){
19527         if(this.elRow){
19528             Roo.fly(this.elRow).addClass(cls);
19529         }
19530         
19531     },
19532     
19533     
19534     removeClass : function(cls){
19535         if(this.elRow){
19536             Roo.fly(this.elRow).removeClass(cls);
19537         }
19538     }
19539
19540     
19541     
19542 });//<Script type="text/javascript">
19543
19544 /*
19545  * Based on:
19546  * Ext JS Library 1.1.1
19547  * Copyright(c) 2006-2007, Ext JS, LLC.
19548  *
19549  * Originally Released Under LGPL - original licence link has changed is not relivant.
19550  *
19551  * Fork - LGPL
19552  * <script type="text/javascript">
19553  */
19554  
19555
19556 /**
19557  * @class Roo.tree.ColumnTree
19558  * @extends Roo.data.TreePanel
19559  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
19560  * @cfg {int} borderWidth  compined right/left border allowance
19561  * @constructor
19562  * @param {String/HTMLElement/Element} el The container element
19563  * @param {Object} config
19564  */
19565 Roo.tree.ColumnTree =  function(el, config)
19566 {
19567    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
19568    this.addEvents({
19569         /**
19570         * @event resize
19571         * Fire this event on a container when it resizes
19572         * @param {int} w Width
19573         * @param {int} h Height
19574         */
19575        "resize" : true
19576     });
19577     this.on('resize', this.onResize, this);
19578 };
19579
19580 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
19581     //lines:false,
19582     
19583     
19584     borderWidth: Roo.isBorderBox ? 0 : 2, 
19585     headEls : false,
19586     
19587     render : function(){
19588         // add the header.....
19589        
19590         Roo.tree.ColumnTree.superclass.render.apply(this);
19591         
19592         this.el.addClass('x-column-tree');
19593         
19594         this.headers = this.el.createChild(
19595             {cls:'x-tree-headers'},this.innerCt.dom);
19596    
19597         var cols = this.columns, c;
19598         var totalWidth = 0;
19599         this.headEls = [];
19600         var  len = cols.length;
19601         for(var i = 0; i < len; i++){
19602              c = cols[i];
19603              totalWidth += c.width;
19604             this.headEls.push(this.headers.createChild({
19605                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19606                  cn: {
19607                      cls:'x-tree-hd-text',
19608                      html: c.header
19609                  },
19610                  style:'width:'+(c.width-this.borderWidth)+'px;'
19611              }));
19612         }
19613         this.headers.createChild({cls:'x-clear'});
19614         // prevent floats from wrapping when clipped
19615         this.headers.setWidth(totalWidth);
19616         //this.innerCt.setWidth(totalWidth);
19617         this.innerCt.setStyle({ overflow: 'auto' });
19618         this.onResize(this.width, this.height);
19619              
19620         
19621     },
19622     onResize : function(w,h)
19623     {
19624         this.height = h;
19625         this.width = w;
19626         // resize cols..
19627         this.innerCt.setWidth(this.width);
19628         this.innerCt.setHeight(this.height-20);
19629         
19630         // headers...
19631         var cols = this.columns, c;
19632         var totalWidth = 0;
19633         var expEl = false;
19634         var len = cols.length;
19635         for(var i = 0; i < len; i++){
19636             c = cols[i];
19637             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19638                 // it's the expander..
19639                 expEl  = this.headEls[i];
19640                 continue;
19641             }
19642             totalWidth += c.width;
19643             
19644         }
19645         if (expEl) {
19646             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
19647         }
19648         this.headers.setWidth(w-20);
19649
19650         
19651         
19652         
19653     }
19654 });
19655 /*
19656  * Based on:
19657  * Ext JS Library 1.1.1
19658  * Copyright(c) 2006-2007, Ext JS, LLC.
19659  *
19660  * Originally Released Under LGPL - original licence link has changed is not relivant.
19661  *
19662  * Fork - LGPL
19663  * <script type="text/javascript">
19664  */
19665  
19666 /**
19667  * @class Roo.menu.Menu
19668  * @extends Roo.util.Observable
19669  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
19670  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19671  * @constructor
19672  * Creates a new Menu
19673  * @param {Object} config Configuration options
19674  */
19675 Roo.menu.Menu = function(config){
19676     Roo.apply(this, config);
19677     this.id = this.id || Roo.id();
19678     this.addEvents({
19679         /**
19680          * @event beforeshow
19681          * Fires before this menu is displayed
19682          * @param {Roo.menu.Menu} this
19683          */
19684         beforeshow : true,
19685         /**
19686          * @event beforehide
19687          * Fires before this menu is hidden
19688          * @param {Roo.menu.Menu} this
19689          */
19690         beforehide : true,
19691         /**
19692          * @event show
19693          * Fires after this menu is displayed
19694          * @param {Roo.menu.Menu} this
19695          */
19696         show : true,
19697         /**
19698          * @event hide
19699          * Fires after this menu is hidden
19700          * @param {Roo.menu.Menu} this
19701          */
19702         hide : true,
19703         /**
19704          * @event click
19705          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19706          * @param {Roo.menu.Menu} this
19707          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19708          * @param {Roo.EventObject} e
19709          */
19710         click : true,
19711         /**
19712          * @event mouseover
19713          * Fires when the mouse is hovering over this menu
19714          * @param {Roo.menu.Menu} this
19715          * @param {Roo.EventObject} e
19716          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19717          */
19718         mouseover : true,
19719         /**
19720          * @event mouseout
19721          * Fires when the mouse exits this menu
19722          * @param {Roo.menu.Menu} this
19723          * @param {Roo.EventObject} e
19724          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19725          */
19726         mouseout : true,
19727         /**
19728          * @event itemclick
19729          * Fires when a menu item contained in this menu is clicked
19730          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19731          * @param {Roo.EventObject} e
19732          */
19733         itemclick: true
19734     });
19735     if (this.registerMenu) {
19736         Roo.menu.MenuMgr.register(this);
19737     }
19738     
19739     var mis = this.items;
19740     this.items = new Roo.util.MixedCollection();
19741     if(mis){
19742         this.add.apply(this, mis);
19743     }
19744 };
19745
19746 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19747     /**
19748      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19749      */
19750     minWidth : 120,
19751     /**
19752      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19753      * for bottom-right shadow (defaults to "sides")
19754      */
19755     shadow : "sides",
19756     /**
19757      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19758      * this menu (defaults to "tl-tr?")
19759      */
19760     subMenuAlign : "tl-tr?",
19761     /**
19762      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19763      * relative to its element of origin (defaults to "tl-bl?")
19764      */
19765     defaultAlign : "tl-bl?",
19766     /**
19767      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19768      */
19769     allowOtherMenus : false,
19770     /**
19771      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19772      */
19773     registerMenu : true,
19774
19775     hidden:true,
19776
19777     // private
19778     render : function(){
19779         if(this.el){
19780             return;
19781         }
19782         var el = this.el = new Roo.Layer({
19783             cls: "x-menu",
19784             shadow:this.shadow,
19785             constrain: false,
19786             parentEl: this.parentEl || document.body,
19787             zindex:15000
19788         });
19789
19790         this.keyNav = new Roo.menu.MenuNav(this);
19791
19792         if(this.plain){
19793             el.addClass("x-menu-plain");
19794         }
19795         if(this.cls){
19796             el.addClass(this.cls);
19797         }
19798         // generic focus element
19799         this.focusEl = el.createChild({
19800             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19801         });
19802         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19803         ul.on("click", this.onClick, this);
19804         ul.on("mouseover", this.onMouseOver, this);
19805         ul.on("mouseout", this.onMouseOut, this);
19806         this.items.each(function(item){
19807             var li = document.createElement("li");
19808             li.className = "x-menu-list-item";
19809             ul.dom.appendChild(li);
19810             item.render(li, this);
19811         }, this);
19812         this.ul = ul;
19813         this.autoWidth();
19814     },
19815
19816     // private
19817     autoWidth : function(){
19818         var el = this.el, ul = this.ul;
19819         if(!el){
19820             return;
19821         }
19822         var w = this.width;
19823         if(w){
19824             el.setWidth(w);
19825         }else if(Roo.isIE){
19826             el.setWidth(this.minWidth);
19827             var t = el.dom.offsetWidth; // force recalc
19828             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19829         }
19830     },
19831
19832     // private
19833     delayAutoWidth : function(){
19834         if(this.rendered){
19835             if(!this.awTask){
19836                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19837             }
19838             this.awTask.delay(20);
19839         }
19840     },
19841
19842     // private
19843     findTargetItem : function(e){
19844         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
19845         if(t && t.menuItemId){
19846             return this.items.get(t.menuItemId);
19847         }
19848     },
19849
19850     // private
19851     onClick : function(e){
19852         var t;
19853         if(t = this.findTargetItem(e)){
19854             t.onClick(e);
19855             this.fireEvent("click", this, t, e);
19856         }
19857     },
19858
19859     // private
19860     setActiveItem : function(item, autoExpand){
19861         if(item != this.activeItem){
19862             if(this.activeItem){
19863                 this.activeItem.deactivate();
19864             }
19865             this.activeItem = item;
19866             item.activate(autoExpand);
19867         }else if(autoExpand){
19868             item.expandMenu();
19869         }
19870     },
19871
19872     // private
19873     tryActivate : function(start, step){
19874         var items = this.items;
19875         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19876             var item = items.get(i);
19877             if(!item.disabled && item.canActivate){
19878                 this.setActiveItem(item, false);
19879                 return item;
19880             }
19881         }
19882         return false;
19883     },
19884
19885     // private
19886     onMouseOver : function(e){
19887         var t;
19888         if(t = this.findTargetItem(e)){
19889             if(t.canActivate && !t.disabled){
19890                 this.setActiveItem(t, true);
19891             }
19892         }
19893         this.fireEvent("mouseover", this, e, t);
19894     },
19895
19896     // private
19897     onMouseOut : function(e){
19898         var t;
19899         if(t = this.findTargetItem(e)){
19900             if(t == this.activeItem && t.shouldDeactivate(e)){
19901                 this.activeItem.deactivate();
19902                 delete this.activeItem;
19903             }
19904         }
19905         this.fireEvent("mouseout", this, e, t);
19906     },
19907
19908     /**
19909      * Read-only.  Returns true if the menu is currently displayed, else false.
19910      * @type Boolean
19911      */
19912     isVisible : function(){
19913         return this.el && !this.hidden;
19914     },
19915
19916     /**
19917      * Displays this menu relative to another element
19918      * @param {String/HTMLElement/Roo.Element} element The element to align to
19919      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
19920      * the element (defaults to this.defaultAlign)
19921      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19922      */
19923     show : function(el, pos, parentMenu){
19924         this.parentMenu = parentMenu;
19925         if(!this.el){
19926             this.render();
19927         }
19928         this.fireEvent("beforeshow", this);
19929         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
19930     },
19931
19932     /**
19933      * Displays this menu at a specific xy position
19934      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
19935      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19936      */
19937     showAt : function(xy, parentMenu, /* private: */_e){
19938         this.parentMenu = parentMenu;
19939         if(!this.el){
19940             this.render();
19941         }
19942         if(_e !== false){
19943             this.fireEvent("beforeshow", this);
19944             xy = this.el.adjustForConstraints(xy);
19945         }
19946         this.el.setXY(xy);
19947         this.el.show();
19948         this.hidden = false;
19949         this.focus();
19950         this.fireEvent("show", this);
19951     },
19952
19953     focus : function(){
19954         if(!this.hidden){
19955             this.doFocus.defer(50, this);
19956         }
19957     },
19958
19959     doFocus : function(){
19960         if(!this.hidden){
19961             this.focusEl.focus();
19962         }
19963     },
19964
19965     /**
19966      * Hides this menu and optionally all parent menus
19967      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
19968      */
19969     hide : function(deep){
19970         if(this.el && this.isVisible()){
19971             this.fireEvent("beforehide", this);
19972             if(this.activeItem){
19973                 this.activeItem.deactivate();
19974                 this.activeItem = null;
19975             }
19976             this.el.hide();
19977             this.hidden = true;
19978             this.fireEvent("hide", this);
19979         }
19980         if(deep === true && this.parentMenu){
19981             this.parentMenu.hide(true);
19982         }
19983     },
19984
19985     /**
19986      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
19987      * Any of the following are valid:
19988      * <ul>
19989      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
19990      * <li>An HTMLElement object which will be converted to a menu item</li>
19991      * <li>A menu item config object that will be created as a new menu item</li>
19992      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
19993      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
19994      * </ul>
19995      * Usage:
19996      * <pre><code>
19997 // Create the menu
19998 var menu = new Roo.menu.Menu();
19999
20000 // Create a menu item to add by reference
20001 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
20002
20003 // Add a bunch of items at once using different methods.
20004 // Only the last item added will be returned.
20005 var item = menu.add(
20006     menuItem,                // add existing item by ref
20007     'Dynamic Item',          // new TextItem
20008     '-',                     // new separator
20009     { text: 'Config Item' }  // new item by config
20010 );
20011 </code></pre>
20012      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
20013      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
20014      */
20015     add : function(){
20016         var a = arguments, l = a.length, item;
20017         for(var i = 0; i < l; i++){
20018             var el = a[i];
20019             if ((typeof(el) == "object") && el.xtype && el.xns) {
20020                 el = Roo.factory(el, Roo.menu);
20021             }
20022             
20023             if(el.render){ // some kind of Item
20024                 item = this.addItem(el);
20025             }else if(typeof el == "string"){ // string
20026                 if(el == "separator" || el == "-"){
20027                     item = this.addSeparator();
20028                 }else{
20029                     item = this.addText(el);
20030                 }
20031             }else if(el.tagName || el.el){ // element
20032                 item = this.addElement(el);
20033             }else if(typeof el == "object"){ // must be menu item config?
20034                 item = this.addMenuItem(el);
20035             }
20036         }
20037         return item;
20038     },
20039
20040     /**
20041      * Returns this menu's underlying {@link Roo.Element} object
20042      * @return {Roo.Element} The element
20043      */
20044     getEl : function(){
20045         if(!this.el){
20046             this.render();
20047         }
20048         return this.el;
20049     },
20050
20051     /**
20052      * Adds a separator bar to the menu
20053      * @return {Roo.menu.Item} The menu item that was added
20054      */
20055     addSeparator : function(){
20056         return this.addItem(new Roo.menu.Separator());
20057     },
20058
20059     /**
20060      * Adds an {@link Roo.Element} object to the menu
20061      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
20062      * @return {Roo.menu.Item} The menu item that was added
20063      */
20064     addElement : function(el){
20065         return this.addItem(new Roo.menu.BaseItem(el));
20066     },
20067
20068     /**
20069      * Adds an existing object based on {@link Roo.menu.Item} to the menu
20070      * @param {Roo.menu.Item} item The menu item to add
20071      * @return {Roo.menu.Item} The menu item that was added
20072      */
20073     addItem : function(item){
20074         this.items.add(item);
20075         if(this.ul){
20076             var li = document.createElement("li");
20077             li.className = "x-menu-list-item";
20078             this.ul.dom.appendChild(li);
20079             item.render(li, this);
20080             this.delayAutoWidth();
20081         }
20082         return item;
20083     },
20084
20085     /**
20086      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
20087      * @param {Object} config A MenuItem config object
20088      * @return {Roo.menu.Item} The menu item that was added
20089      */
20090     addMenuItem : function(config){
20091         if(!(config instanceof Roo.menu.Item)){
20092             if(typeof config.checked == "boolean"){ // must be check menu item config?
20093                 config = new Roo.menu.CheckItem(config);
20094             }else{
20095                 config = new Roo.menu.Item(config);
20096             }
20097         }
20098         return this.addItem(config);
20099     },
20100
20101     /**
20102      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
20103      * @param {String} text The text to display in the menu item
20104      * @return {Roo.menu.Item} The menu item that was added
20105      */
20106     addText : function(text){
20107         return this.addItem(new Roo.menu.TextItem({ text : text }));
20108     },
20109
20110     /**
20111      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
20112      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
20113      * @param {Roo.menu.Item} item The menu item to add
20114      * @return {Roo.menu.Item} The menu item that was added
20115      */
20116     insert : function(index, item){
20117         this.items.insert(index, item);
20118         if(this.ul){
20119             var li = document.createElement("li");
20120             li.className = "x-menu-list-item";
20121             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
20122             item.render(li, this);
20123             this.delayAutoWidth();
20124         }
20125         return item;
20126     },
20127
20128     /**
20129      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
20130      * @param {Roo.menu.Item} item The menu item to remove
20131      */
20132     remove : function(item){
20133         this.items.removeKey(item.id);
20134         item.destroy();
20135     },
20136
20137     /**
20138      * Removes and destroys all items in the menu
20139      */
20140     removeAll : function(){
20141         var f;
20142         while(f = this.items.first()){
20143             this.remove(f);
20144         }
20145     }
20146 });
20147
20148 // MenuNav is a private utility class used internally by the Menu
20149 Roo.menu.MenuNav = function(menu){
20150     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
20151     this.scope = this.menu = menu;
20152 };
20153
20154 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
20155     doRelay : function(e, h){
20156         var k = e.getKey();
20157         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
20158             this.menu.tryActivate(0, 1);
20159             return false;
20160         }
20161         return h.call(this.scope || this, e, this.menu);
20162     },
20163
20164     up : function(e, m){
20165         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
20166             m.tryActivate(m.items.length-1, -1);
20167         }
20168     },
20169
20170     down : function(e, m){
20171         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
20172             m.tryActivate(0, 1);
20173         }
20174     },
20175
20176     right : function(e, m){
20177         if(m.activeItem){
20178             m.activeItem.expandMenu(true);
20179         }
20180     },
20181
20182     left : function(e, m){
20183         m.hide();
20184         if(m.parentMenu && m.parentMenu.activeItem){
20185             m.parentMenu.activeItem.activate();
20186         }
20187     },
20188
20189     enter : function(e, m){
20190         if(m.activeItem){
20191             e.stopPropagation();
20192             m.activeItem.onClick(e);
20193             m.fireEvent("click", this, m.activeItem);
20194             return true;
20195         }
20196     }
20197 });/*
20198  * Based on:
20199  * Ext JS Library 1.1.1
20200  * Copyright(c) 2006-2007, Ext JS, LLC.
20201  *
20202  * Originally Released Under LGPL - original licence link has changed is not relivant.
20203  *
20204  * Fork - LGPL
20205  * <script type="text/javascript">
20206  */
20207  
20208 /**
20209  * @class Roo.menu.MenuMgr
20210  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
20211  * @singleton
20212  */
20213 Roo.menu.MenuMgr = function(){
20214    var menus, active, groups = {}, attached = false, lastShow = new Date();
20215
20216    // private - called when first menu is created
20217    function init(){
20218        menus = {};
20219        active = new Roo.util.MixedCollection();
20220        Roo.get(document).addKeyListener(27, function(){
20221            if(active.length > 0){
20222                hideAll();
20223            }
20224        });
20225    }
20226
20227    // private
20228    function hideAll(){
20229        if(active && active.length > 0){
20230            var c = active.clone();
20231            c.each(function(m){
20232                m.hide();
20233            });
20234        }
20235    }
20236
20237    // private
20238    function onHide(m){
20239        active.remove(m);
20240        if(active.length < 1){
20241            Roo.get(document).un("mousedown", onMouseDown);
20242            attached = false;
20243        }
20244    }
20245
20246    // private
20247    function onShow(m){
20248        var last = active.last();
20249        lastShow = new Date();
20250        active.add(m);
20251        if(!attached){
20252            Roo.get(document).on("mousedown", onMouseDown);
20253            attached = true;
20254        }
20255        if(m.parentMenu){
20256           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
20257           m.parentMenu.activeChild = m;
20258        }else if(last && last.isVisible()){
20259           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
20260        }
20261    }
20262
20263    // private
20264    function onBeforeHide(m){
20265        if(m.activeChild){
20266            m.activeChild.hide();
20267        }
20268        if(m.autoHideTimer){
20269            clearTimeout(m.autoHideTimer);
20270            delete m.autoHideTimer;
20271        }
20272    }
20273
20274    // private
20275    function onBeforeShow(m){
20276        var pm = m.parentMenu;
20277        if(!pm && !m.allowOtherMenus){
20278            hideAll();
20279        }else if(pm && pm.activeChild && active != m){
20280            pm.activeChild.hide();
20281        }
20282    }
20283
20284    // private
20285    function onMouseDown(e){
20286        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
20287            hideAll();
20288        }
20289    }
20290
20291    // private
20292    function onBeforeCheck(mi, state){
20293        if(state){
20294            var g = groups[mi.group];
20295            for(var i = 0, l = g.length; i < l; i++){
20296                if(g[i] != mi){
20297                    g[i].setChecked(false);
20298                }
20299            }
20300        }
20301    }
20302
20303    return {
20304
20305        /**
20306         * Hides all menus that are currently visible
20307         */
20308        hideAll : function(){
20309             hideAll();  
20310        },
20311
20312        // private
20313        register : function(menu){
20314            if(!menus){
20315                init();
20316            }
20317            menus[menu.id] = menu;
20318            menu.on("beforehide", onBeforeHide);
20319            menu.on("hide", onHide);
20320            menu.on("beforeshow", onBeforeShow);
20321            menu.on("show", onShow);
20322            var g = menu.group;
20323            if(g && menu.events["checkchange"]){
20324                if(!groups[g]){
20325                    groups[g] = [];
20326                }
20327                groups[g].push(menu);
20328                menu.on("checkchange", onCheck);
20329            }
20330        },
20331
20332         /**
20333          * Returns a {@link Roo.menu.Menu} object
20334          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
20335          * be used to generate and return a new Menu instance.
20336          */
20337        get : function(menu){
20338            if(typeof menu == "string"){ // menu id
20339                return menus[menu];
20340            }else if(menu.events){  // menu instance
20341                return menu;
20342            }else if(typeof menu.length == 'number'){ // array of menu items?
20343                return new Roo.menu.Menu({items:menu});
20344            }else{ // otherwise, must be a config
20345                return new Roo.menu.Menu(menu);
20346            }
20347        },
20348
20349        // private
20350        unregister : function(menu){
20351            delete menus[menu.id];
20352            menu.un("beforehide", onBeforeHide);
20353            menu.un("hide", onHide);
20354            menu.un("beforeshow", onBeforeShow);
20355            menu.un("show", onShow);
20356            var g = menu.group;
20357            if(g && menu.events["checkchange"]){
20358                groups[g].remove(menu);
20359                menu.un("checkchange", onCheck);
20360            }
20361        },
20362
20363        // private
20364        registerCheckable : function(menuItem){
20365            var g = menuItem.group;
20366            if(g){
20367                if(!groups[g]){
20368                    groups[g] = [];
20369                }
20370                groups[g].push(menuItem);
20371                menuItem.on("beforecheckchange", onBeforeCheck);
20372            }
20373        },
20374
20375        // private
20376        unregisterCheckable : function(menuItem){
20377            var g = menuItem.group;
20378            if(g){
20379                groups[g].remove(menuItem);
20380                menuItem.un("beforecheckchange", onBeforeCheck);
20381            }
20382        }
20383    };
20384 }();/*
20385  * Based on:
20386  * Ext JS Library 1.1.1
20387  * Copyright(c) 2006-2007, Ext JS, LLC.
20388  *
20389  * Originally Released Under LGPL - original licence link has changed is not relivant.
20390  *
20391  * Fork - LGPL
20392  * <script type="text/javascript">
20393  */
20394  
20395
20396 /**
20397  * @class Roo.menu.BaseItem
20398  * @extends Roo.Component
20399  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
20400  * management and base configuration options shared by all menu components.
20401  * @constructor
20402  * Creates a new BaseItem
20403  * @param {Object} config Configuration options
20404  */
20405 Roo.menu.BaseItem = function(config){
20406     Roo.menu.BaseItem.superclass.constructor.call(this, config);
20407
20408     this.addEvents({
20409         /**
20410          * @event click
20411          * Fires when this item is clicked
20412          * @param {Roo.menu.BaseItem} this
20413          * @param {Roo.EventObject} e
20414          */
20415         click: true,
20416         /**
20417          * @event activate
20418          * Fires when this item is activated
20419          * @param {Roo.menu.BaseItem} this
20420          */
20421         activate : true,
20422         /**
20423          * @event deactivate
20424          * Fires when this item is deactivated
20425          * @param {Roo.menu.BaseItem} this
20426          */
20427         deactivate : true
20428     });
20429
20430     if(this.handler){
20431         this.on("click", this.handler, this.scope, true);
20432     }
20433 };
20434
20435 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
20436     /**
20437      * @cfg {Function} handler
20438      * A function that will handle the click event of this menu item (defaults to undefined)
20439      */
20440     /**
20441      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
20442      */
20443     canActivate : false,
20444     /**
20445      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
20446      */
20447     activeClass : "x-menu-item-active",
20448     /**
20449      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
20450      */
20451     hideOnClick : true,
20452     /**
20453      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
20454      */
20455     hideDelay : 100,
20456
20457     // private
20458     ctype: "Roo.menu.BaseItem",
20459
20460     // private
20461     actionMode : "container",
20462
20463     // private
20464     render : function(container, parentMenu){
20465         this.parentMenu = parentMenu;
20466         Roo.menu.BaseItem.superclass.render.call(this, container);
20467         this.container.menuItemId = this.id;
20468     },
20469
20470     // private
20471     onRender : function(container, position){
20472         this.el = Roo.get(this.el);
20473         container.dom.appendChild(this.el.dom);
20474     },
20475
20476     // private
20477     onClick : function(e){
20478         if(!this.disabled && this.fireEvent("click", this, e) !== false
20479                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
20480             this.handleClick(e);
20481         }else{
20482             e.stopEvent();
20483         }
20484     },
20485
20486     // private
20487     activate : function(){
20488         if(this.disabled){
20489             return false;
20490         }
20491         var li = this.container;
20492         li.addClass(this.activeClass);
20493         this.region = li.getRegion().adjust(2, 2, -2, -2);
20494         this.fireEvent("activate", this);
20495         return true;
20496     },
20497
20498     // private
20499     deactivate : function(){
20500         this.container.removeClass(this.activeClass);
20501         this.fireEvent("deactivate", this);
20502     },
20503
20504     // private
20505     shouldDeactivate : function(e){
20506         return !this.region || !this.region.contains(e.getPoint());
20507     },
20508
20509     // private
20510     handleClick : function(e){
20511         if(this.hideOnClick){
20512             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
20513         }
20514     },
20515
20516     // private
20517     expandMenu : function(autoActivate){
20518         // do nothing
20519     },
20520
20521     // private
20522     hideMenu : function(){
20523         // do nothing
20524     }
20525 });/*
20526  * Based on:
20527  * Ext JS Library 1.1.1
20528  * Copyright(c) 2006-2007, Ext JS, LLC.
20529  *
20530  * Originally Released Under LGPL - original licence link has changed is not relivant.
20531  *
20532  * Fork - LGPL
20533  * <script type="text/javascript">
20534  */
20535  
20536 /**
20537  * @class Roo.menu.Adapter
20538  * @extends Roo.menu.BaseItem
20539  * 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.
20540  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
20541  * @constructor
20542  * Creates a new Adapter
20543  * @param {Object} config Configuration options
20544  */
20545 Roo.menu.Adapter = function(component, config){
20546     Roo.menu.Adapter.superclass.constructor.call(this, config);
20547     this.component = component;
20548 };
20549 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
20550     // private
20551     canActivate : true,
20552
20553     // private
20554     onRender : function(container, position){
20555         this.component.render(container);
20556         this.el = this.component.getEl();
20557     },
20558
20559     // private
20560     activate : function(){
20561         if(this.disabled){
20562             return false;
20563         }
20564         this.component.focus();
20565         this.fireEvent("activate", this);
20566         return true;
20567     },
20568
20569     // private
20570     deactivate : function(){
20571         this.fireEvent("deactivate", this);
20572     },
20573
20574     // private
20575     disable : function(){
20576         this.component.disable();
20577         Roo.menu.Adapter.superclass.disable.call(this);
20578     },
20579
20580     // private
20581     enable : function(){
20582         this.component.enable();
20583         Roo.menu.Adapter.superclass.enable.call(this);
20584     }
20585 });/*
20586  * Based on:
20587  * Ext JS Library 1.1.1
20588  * Copyright(c) 2006-2007, Ext JS, LLC.
20589  *
20590  * Originally Released Under LGPL - original licence link has changed is not relivant.
20591  *
20592  * Fork - LGPL
20593  * <script type="text/javascript">
20594  */
20595
20596 /**
20597  * @class Roo.menu.TextItem
20598  * @extends Roo.menu.BaseItem
20599  * Adds a static text string to a menu, usually used as either a heading or group separator.
20600  * Note: old style constructor with text is still supported.
20601  * 
20602  * @constructor
20603  * Creates a new TextItem
20604  * @param {Object} cfg Configuration
20605  */
20606 Roo.menu.TextItem = function(cfg){
20607     if (typeof(cfg) == 'string') {
20608         this.text = cfg;
20609     } else {
20610         Roo.apply(this,cfg);
20611     }
20612     
20613     Roo.menu.TextItem.superclass.constructor.call(this);
20614 };
20615
20616 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20617     /**
20618      * @cfg {Boolean} text Text to show on item.
20619      */
20620     text : '',
20621     
20622     /**
20623      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20624      */
20625     hideOnClick : false,
20626     /**
20627      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20628      */
20629     itemCls : "x-menu-text",
20630
20631     // private
20632     onRender : function(){
20633         var s = document.createElement("span");
20634         s.className = this.itemCls;
20635         s.innerHTML = this.text;
20636         this.el = s;
20637         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20638     }
20639 });/*
20640  * Based on:
20641  * Ext JS Library 1.1.1
20642  * Copyright(c) 2006-2007, Ext JS, LLC.
20643  *
20644  * Originally Released Under LGPL - original licence link has changed is not relivant.
20645  *
20646  * Fork - LGPL
20647  * <script type="text/javascript">
20648  */
20649
20650 /**
20651  * @class Roo.menu.Separator
20652  * @extends Roo.menu.BaseItem
20653  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20654  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20655  * @constructor
20656  * @param {Object} config Configuration options
20657  */
20658 Roo.menu.Separator = function(config){
20659     Roo.menu.Separator.superclass.constructor.call(this, config);
20660 };
20661
20662 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20663     /**
20664      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20665      */
20666     itemCls : "x-menu-sep",
20667     /**
20668      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20669      */
20670     hideOnClick : false,
20671
20672     // private
20673     onRender : function(li){
20674         var s = document.createElement("span");
20675         s.className = this.itemCls;
20676         s.innerHTML = "&#160;";
20677         this.el = s;
20678         li.addClass("x-menu-sep-li");
20679         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20680     }
20681 });/*
20682  * Based on:
20683  * Ext JS Library 1.1.1
20684  * Copyright(c) 2006-2007, Ext JS, LLC.
20685  *
20686  * Originally Released Under LGPL - original licence link has changed is not relivant.
20687  *
20688  * Fork - LGPL
20689  * <script type="text/javascript">
20690  */
20691 /**
20692  * @class Roo.menu.Item
20693  * @extends Roo.menu.BaseItem
20694  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20695  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20696  * activation and click handling.
20697  * @constructor
20698  * Creates a new Item
20699  * @param {Object} config Configuration options
20700  */
20701 Roo.menu.Item = function(config){
20702     Roo.menu.Item.superclass.constructor.call(this, config);
20703     if(this.menu){
20704         this.menu = Roo.menu.MenuMgr.get(this.menu);
20705     }
20706 };
20707 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20708     
20709     /**
20710      * @cfg {String} text
20711      * The text to show on the menu item.
20712      */
20713     text: '',
20714      /**
20715      * @cfg {String} HTML to render in menu
20716      * The text to show on the menu item (HTML version).
20717      */
20718     html: '',
20719     /**
20720      * @cfg {String} icon
20721      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20722      */
20723     icon: undefined,
20724     /**
20725      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20726      */
20727     itemCls : "x-menu-item",
20728     /**
20729      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20730      */
20731     canActivate : true,
20732     /**
20733      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20734      */
20735     showDelay: 200,
20736     // doc'd in BaseItem
20737     hideDelay: 200,
20738
20739     // private
20740     ctype: "Roo.menu.Item",
20741     
20742     // private
20743     onRender : function(container, position){
20744         var el = document.createElement("a");
20745         el.hideFocus = true;
20746         el.unselectable = "on";
20747         el.href = this.href || "#";
20748         if(this.hrefTarget){
20749             el.target = this.hrefTarget;
20750         }
20751         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
20752         
20753         var html = this.html.length ? this.html  : String.format('{0}',this.text);
20754         
20755         el.innerHTML = String.format(
20756                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
20757                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
20758         this.el = el;
20759         Roo.menu.Item.superclass.onRender.call(this, container, position);
20760     },
20761
20762     /**
20763      * Sets the text to display in this menu item
20764      * @param {String} text The text to display
20765      * @param {Boolean} isHTML true to indicate text is pure html.
20766      */
20767     setText : function(text, isHTML){
20768         if (isHTML) {
20769             this.html = text;
20770         } else {
20771             this.text = text;
20772             this.html = '';
20773         }
20774         if(this.rendered){
20775             var html = this.html.length ? this.html  : String.format('{0}',this.text);
20776      
20777             this.el.update(String.format(
20778                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
20779                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20780             this.parentMenu.autoWidth();
20781         }
20782     },
20783
20784     // private
20785     handleClick : function(e){
20786         if(!this.href){ // if no link defined, stop the event automatically
20787             e.stopEvent();
20788         }
20789         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20790     },
20791
20792     // private
20793     activate : function(autoExpand){
20794         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20795             this.focus();
20796             if(autoExpand){
20797                 this.expandMenu();
20798             }
20799         }
20800         return true;
20801     },
20802
20803     // private
20804     shouldDeactivate : function(e){
20805         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20806             if(this.menu && this.menu.isVisible()){
20807                 return !this.menu.getEl().getRegion().contains(e.getPoint());
20808             }
20809             return true;
20810         }
20811         return false;
20812     },
20813
20814     // private
20815     deactivate : function(){
20816         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20817         this.hideMenu();
20818     },
20819
20820     // private
20821     expandMenu : function(autoActivate){
20822         if(!this.disabled && this.menu){
20823             clearTimeout(this.hideTimer);
20824             delete this.hideTimer;
20825             if(!this.menu.isVisible() && !this.showTimer){
20826                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20827             }else if (this.menu.isVisible() && autoActivate){
20828                 this.menu.tryActivate(0, 1);
20829             }
20830         }
20831     },
20832
20833     // private
20834     deferExpand : function(autoActivate){
20835         delete this.showTimer;
20836         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20837         if(autoActivate){
20838             this.menu.tryActivate(0, 1);
20839         }
20840     },
20841
20842     // private
20843     hideMenu : function(){
20844         clearTimeout(this.showTimer);
20845         delete this.showTimer;
20846         if(!this.hideTimer && this.menu && this.menu.isVisible()){
20847             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20848         }
20849     },
20850
20851     // private
20852     deferHide : function(){
20853         delete this.hideTimer;
20854         this.menu.hide();
20855     }
20856 });/*
20857  * Based on:
20858  * Ext JS Library 1.1.1
20859  * Copyright(c) 2006-2007, Ext JS, LLC.
20860  *
20861  * Originally Released Under LGPL - original licence link has changed is not relivant.
20862  *
20863  * Fork - LGPL
20864  * <script type="text/javascript">
20865  */
20866  
20867 /**
20868  * @class Roo.menu.CheckItem
20869  * @extends Roo.menu.Item
20870  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20871  * @constructor
20872  * Creates a new CheckItem
20873  * @param {Object} config Configuration options
20874  */
20875 Roo.menu.CheckItem = function(config){
20876     Roo.menu.CheckItem.superclass.constructor.call(this, config);
20877     this.addEvents({
20878         /**
20879          * @event beforecheckchange
20880          * Fires before the checked value is set, providing an opportunity to cancel if needed
20881          * @param {Roo.menu.CheckItem} this
20882          * @param {Boolean} checked The new checked value that will be set
20883          */
20884         "beforecheckchange" : true,
20885         /**
20886          * @event checkchange
20887          * Fires after the checked value has been set
20888          * @param {Roo.menu.CheckItem} this
20889          * @param {Boolean} checked The checked value that was set
20890          */
20891         "checkchange" : true
20892     });
20893     if(this.checkHandler){
20894         this.on('checkchange', this.checkHandler, this.scope);
20895     }
20896 };
20897 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
20898     /**
20899      * @cfg {String} group
20900      * All check items with the same group name will automatically be grouped into a single-select
20901      * radio button group (defaults to '')
20902      */
20903     /**
20904      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
20905      */
20906     itemCls : "x-menu-item x-menu-check-item",
20907     /**
20908      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
20909      */
20910     groupClass : "x-menu-group-item",
20911
20912     /**
20913      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
20914      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
20915      * initialized with checked = true will be rendered as checked.
20916      */
20917     checked: false,
20918
20919     // private
20920     ctype: "Roo.menu.CheckItem",
20921
20922     // private
20923     onRender : function(c){
20924         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
20925         if(this.group){
20926             this.el.addClass(this.groupClass);
20927         }
20928         Roo.menu.MenuMgr.registerCheckable(this);
20929         if(this.checked){
20930             this.checked = false;
20931             this.setChecked(true, true);
20932         }
20933     },
20934
20935     // private
20936     destroy : function(){
20937         if(this.rendered){
20938             Roo.menu.MenuMgr.unregisterCheckable(this);
20939         }
20940         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
20941     },
20942
20943     /**
20944      * Set the checked state of this item
20945      * @param {Boolean} checked The new checked value
20946      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
20947      */
20948     setChecked : function(state, suppressEvent){
20949         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
20950             if(this.container){
20951                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
20952             }
20953             this.checked = state;
20954             if(suppressEvent !== true){
20955                 this.fireEvent("checkchange", this, state);
20956             }
20957         }
20958     },
20959
20960     // private
20961     handleClick : function(e){
20962        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
20963            this.setChecked(!this.checked);
20964        }
20965        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
20966     }
20967 });/*
20968  * Based on:
20969  * Ext JS Library 1.1.1
20970  * Copyright(c) 2006-2007, Ext JS, LLC.
20971  *
20972  * Originally Released Under LGPL - original licence link has changed is not relivant.
20973  *
20974  * Fork - LGPL
20975  * <script type="text/javascript">
20976  */
20977  
20978 /**
20979  * @class Roo.menu.DateItem
20980  * @extends Roo.menu.Adapter
20981  * A menu item that wraps the {@link Roo.DatPicker} component.
20982  * @constructor
20983  * Creates a new DateItem
20984  * @param {Object} config Configuration options
20985  */
20986 Roo.menu.DateItem = function(config){
20987     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
20988     /** The Roo.DatePicker object @type Roo.DatePicker */
20989     this.picker = this.component;
20990     this.addEvents({select: true});
20991     
20992     this.picker.on("render", function(picker){
20993         picker.getEl().swallowEvent("click");
20994         picker.container.addClass("x-menu-date-item");
20995     });
20996
20997     this.picker.on("select", this.onSelect, this);
20998 };
20999
21000 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
21001     // private
21002     onSelect : function(picker, date){
21003         this.fireEvent("select", this, date, picker);
21004         Roo.menu.DateItem.superclass.handleClick.call(this);
21005     }
21006 });/*
21007  * Based on:
21008  * Ext JS Library 1.1.1
21009  * Copyright(c) 2006-2007, Ext JS, LLC.
21010  *
21011  * Originally Released Under LGPL - original licence link has changed is not relivant.
21012  *
21013  * Fork - LGPL
21014  * <script type="text/javascript">
21015  */
21016  
21017 /**
21018  * @class Roo.menu.ColorItem
21019  * @extends Roo.menu.Adapter
21020  * A menu item that wraps the {@link Roo.ColorPalette} component.
21021  * @constructor
21022  * Creates a new ColorItem
21023  * @param {Object} config Configuration options
21024  */
21025 Roo.menu.ColorItem = function(config){
21026     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
21027     /** The Roo.ColorPalette object @type Roo.ColorPalette */
21028     this.palette = this.component;
21029     this.relayEvents(this.palette, ["select"]);
21030     if(this.selectHandler){
21031         this.on('select', this.selectHandler, this.scope);
21032     }
21033 };
21034 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
21035  * Based on:
21036  * Ext JS Library 1.1.1
21037  * Copyright(c) 2006-2007, Ext JS, LLC.
21038  *
21039  * Originally Released Under LGPL - original licence link has changed is not relivant.
21040  *
21041  * Fork - LGPL
21042  * <script type="text/javascript">
21043  */
21044  
21045
21046 /**
21047  * @class Roo.menu.DateMenu
21048  * @extends Roo.menu.Menu
21049  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
21050  * @constructor
21051  * Creates a new DateMenu
21052  * @param {Object} config Configuration options
21053  */
21054 Roo.menu.DateMenu = function(config){
21055     Roo.menu.DateMenu.superclass.constructor.call(this, config);
21056     this.plain = true;
21057     var di = new Roo.menu.DateItem(config);
21058     this.add(di);
21059     /**
21060      * The {@link Roo.DatePicker} instance for this DateMenu
21061      * @type DatePicker
21062      */
21063     this.picker = di.picker;
21064     /**
21065      * @event select
21066      * @param {DatePicker} picker
21067      * @param {Date} date
21068      */
21069     this.relayEvents(di, ["select"]);
21070     this.on('beforeshow', function(){
21071         if(this.picker){
21072             this.picker.hideMonthPicker(false);
21073         }
21074     }, this);
21075 };
21076 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
21077     cls:'x-date-menu'
21078 });/*
21079  * Based on:
21080  * Ext JS Library 1.1.1
21081  * Copyright(c) 2006-2007, Ext JS, LLC.
21082  *
21083  * Originally Released Under LGPL - original licence link has changed is not relivant.
21084  *
21085  * Fork - LGPL
21086  * <script type="text/javascript">
21087  */
21088  
21089
21090 /**
21091  * @class Roo.menu.ColorMenu
21092  * @extends Roo.menu.Menu
21093  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
21094  * @constructor
21095  * Creates a new ColorMenu
21096  * @param {Object} config Configuration options
21097  */
21098 Roo.menu.ColorMenu = function(config){
21099     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
21100     this.plain = true;
21101     var ci = new Roo.menu.ColorItem(config);
21102     this.add(ci);
21103     /**
21104      * The {@link Roo.ColorPalette} instance for this ColorMenu
21105      * @type ColorPalette
21106      */
21107     this.palette = ci.palette;
21108     /**
21109      * @event select
21110      * @param {ColorPalette} palette
21111      * @param {String} color
21112      */
21113     this.relayEvents(ci, ["select"]);
21114 };
21115 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
21116  * Based on:
21117  * Ext JS Library 1.1.1
21118  * Copyright(c) 2006-2007, Ext JS, LLC.
21119  *
21120  * Originally Released Under LGPL - original licence link has changed is not relivant.
21121  *
21122  * Fork - LGPL
21123  * <script type="text/javascript">
21124  */
21125  
21126 /**
21127  * @class Roo.form.Field
21128  * @extends Roo.BoxComponent
21129  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
21130  * @constructor
21131  * Creates a new Field
21132  * @param {Object} config Configuration options
21133  */
21134 Roo.form.Field = function(config){
21135     Roo.form.Field.superclass.constructor.call(this, config);
21136 };
21137
21138 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
21139     /**
21140      * @cfg {String} fieldLabel Label to use when rendering a form.
21141      */
21142        /**
21143      * @cfg {String} qtip Mouse over tip
21144      */
21145      
21146     /**
21147      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
21148      */
21149     invalidClass : "x-form-invalid",
21150     /**
21151      * @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")
21152      */
21153     invalidText : "The value in this field is invalid",
21154     /**
21155      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
21156      */
21157     focusClass : "x-form-focus",
21158     /**
21159      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
21160       automatic validation (defaults to "keyup").
21161      */
21162     validationEvent : "keyup",
21163     /**
21164      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
21165      */
21166     validateOnBlur : true,
21167     /**
21168      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
21169      */
21170     validationDelay : 250,
21171     /**
21172      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21173      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
21174      */
21175     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
21176     /**
21177      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
21178      */
21179     fieldClass : "x-form-field",
21180     /**
21181      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
21182      *<pre>
21183 Value         Description
21184 -----------   ----------------------------------------------------------------------
21185 qtip          Display a quick tip when the user hovers over the field
21186 title         Display a default browser title attribute popup
21187 under         Add a block div beneath the field containing the error text
21188 side          Add an error icon to the right of the field with a popup on hover
21189 [element id]  Add the error text directly to the innerHTML of the specified element
21190 </pre>
21191      */
21192     msgTarget : 'qtip',
21193     /**
21194      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
21195      */
21196     msgFx : 'normal',
21197
21198     /**
21199      * @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.
21200      */
21201     readOnly : false,
21202
21203     /**
21204      * @cfg {Boolean} disabled True to disable the field (defaults to false).
21205      */
21206     disabled : false,
21207
21208     /**
21209      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
21210      */
21211     inputType : undefined,
21212     
21213     /**
21214      * @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).
21215          */
21216         tabIndex : undefined,
21217         
21218     // private
21219     isFormField : true,
21220
21221     // private
21222     hasFocus : false,
21223     /**
21224      * @property {Roo.Element} fieldEl
21225      * Element Containing the rendered Field (with label etc.)
21226      */
21227     /**
21228      * @cfg {Mixed} value A value to initialize this field with.
21229      */
21230     value : undefined,
21231
21232     /**
21233      * @cfg {String} name The field's HTML name attribute.
21234      */
21235     /**
21236      * @cfg {String} cls A CSS class to apply to the field's underlying element.
21237      */
21238
21239         // private ??
21240         initComponent : function(){
21241         Roo.form.Field.superclass.initComponent.call(this);
21242         this.addEvents({
21243             /**
21244              * @event focus
21245              * Fires when this field receives input focus.
21246              * @param {Roo.form.Field} this
21247              */
21248             focus : true,
21249             /**
21250              * @event blur
21251              * Fires when this field loses input focus.
21252              * @param {Roo.form.Field} this
21253              */
21254             blur : true,
21255             /**
21256              * @event specialkey
21257              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
21258              * {@link Roo.EventObject#getKey} to determine which key was pressed.
21259              * @param {Roo.form.Field} this
21260              * @param {Roo.EventObject} e The event object
21261              */
21262             specialkey : true,
21263             /**
21264              * @event change
21265              * Fires just before the field blurs if the field value has changed.
21266              * @param {Roo.form.Field} this
21267              * @param {Mixed} newValue The new value
21268              * @param {Mixed} oldValue The original value
21269              */
21270             change : true,
21271             /**
21272              * @event invalid
21273              * Fires after the field has been marked as invalid.
21274              * @param {Roo.form.Field} this
21275              * @param {String} msg The validation message
21276              */
21277             invalid : true,
21278             /**
21279              * @event valid
21280              * Fires after the field has been validated with no errors.
21281              * @param {Roo.form.Field} this
21282              */
21283             valid : true,
21284              /**
21285              * @event keyup
21286              * Fires after the key up
21287              * @param {Roo.form.Field} this
21288              * @param {Roo.EventObject}  e The event Object
21289              */
21290             keyup : true
21291         });
21292     },
21293
21294     /**
21295      * Returns the name attribute of the field if available
21296      * @return {String} name The field name
21297      */
21298     getName: function(){
21299          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
21300     },
21301
21302     // private
21303     onRender : function(ct, position){
21304         Roo.form.Field.superclass.onRender.call(this, ct, position);
21305         if(!this.el){
21306             var cfg = this.getAutoCreate();
21307             if(!cfg.name){
21308                 cfg.name = this.name || this.id;
21309             }
21310             if(this.inputType){
21311                 cfg.type = this.inputType;
21312             }
21313             this.el = ct.createChild(cfg, position);
21314         }
21315         var type = this.el.dom.type;
21316         if(type){
21317             if(type == 'password'){
21318                 type = 'text';
21319             }
21320             this.el.addClass('x-form-'+type);
21321         }
21322         if(this.readOnly){
21323             this.el.dom.readOnly = true;
21324         }
21325         if(this.tabIndex !== undefined){
21326             this.el.dom.setAttribute('tabIndex', this.tabIndex);
21327         }
21328
21329         this.el.addClass([this.fieldClass, this.cls]);
21330         this.initValue();
21331     },
21332
21333     /**
21334      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
21335      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
21336      * @return {Roo.form.Field} this
21337      */
21338     applyTo : function(target){
21339         this.allowDomMove = false;
21340         this.el = Roo.get(target);
21341         this.render(this.el.dom.parentNode);
21342         return this;
21343     },
21344
21345     // private
21346     initValue : function(){
21347         if(this.value !== undefined){
21348             this.setValue(this.value);
21349         }else if(this.el.dom.value.length > 0){
21350             this.setValue(this.el.dom.value);
21351         }
21352     },
21353
21354     /**
21355      * Returns true if this field has been changed since it was originally loaded and is not disabled.
21356      */
21357     isDirty : function() {
21358         if(this.disabled) {
21359             return false;
21360         }
21361         return String(this.getValue()) !== String(this.originalValue);
21362     },
21363
21364     // private
21365     afterRender : function(){
21366         Roo.form.Field.superclass.afterRender.call(this);
21367         this.initEvents();
21368     },
21369
21370     // private
21371     fireKey : function(e){
21372         //Roo.log('field ' + e.getKey());
21373         if(e.isNavKeyPress()){
21374             this.fireEvent("specialkey", this, e);
21375         }
21376     },
21377
21378     /**
21379      * Resets the current field value to the originally loaded value and clears any validation messages
21380      */
21381     reset : function(){
21382         this.setValue(this.originalValue);
21383         this.clearInvalid();
21384     },
21385
21386     // private
21387     initEvents : function(){
21388         // safari killled keypress - so keydown is now used..
21389         this.el.on("keydown" , this.fireKey,  this);
21390         this.el.on("focus", this.onFocus,  this);
21391         this.el.on("blur", this.onBlur,  this);
21392         this.el.relayEvent('keyup', this);
21393
21394         // reference to original value for reset
21395         this.originalValue = this.getValue();
21396     },
21397
21398     // private
21399     onFocus : function(){
21400         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21401             this.el.addClass(this.focusClass);
21402         }
21403         if(!this.hasFocus){
21404             this.hasFocus = true;
21405             this.startValue = this.getValue();
21406             this.fireEvent("focus", this);
21407         }
21408     },
21409
21410     beforeBlur : Roo.emptyFn,
21411
21412     // private
21413     onBlur : function(){
21414         this.beforeBlur();
21415         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21416             this.el.removeClass(this.focusClass);
21417         }
21418         this.hasFocus = false;
21419         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
21420             this.validate();
21421         }
21422         var v = this.getValue();
21423         if(String(v) !== String(this.startValue)){
21424             this.fireEvent('change', this, v, this.startValue);
21425         }
21426         this.fireEvent("blur", this);
21427     },
21428
21429     /**
21430      * Returns whether or not the field value is currently valid
21431      * @param {Boolean} preventMark True to disable marking the field invalid
21432      * @return {Boolean} True if the value is valid, else false
21433      */
21434     isValid : function(preventMark){
21435         if(this.disabled){
21436             return true;
21437         }
21438         var restore = this.preventMark;
21439         this.preventMark = preventMark === true;
21440         var v = this.validateValue(this.processValue(this.getRawValue()));
21441         this.preventMark = restore;
21442         return v;
21443     },
21444
21445     /**
21446      * Validates the field value
21447      * @return {Boolean} True if the value is valid, else false
21448      */
21449     validate : function(){
21450         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
21451             this.clearInvalid();
21452             return true;
21453         }
21454         return false;
21455     },
21456
21457     processValue : function(value){
21458         return value;
21459     },
21460
21461     // private
21462     // Subclasses should provide the validation implementation by overriding this
21463     validateValue : function(value){
21464         return true;
21465     },
21466
21467     /**
21468      * Mark this field as invalid
21469      * @param {String} msg The validation message
21470      */
21471     markInvalid : function(msg){
21472         if(!this.rendered || this.preventMark){ // not rendered
21473             return;
21474         }
21475         this.el.addClass(this.invalidClass);
21476         msg = msg || this.invalidText;
21477         switch(this.msgTarget){
21478             case 'qtip':
21479                 this.el.dom.qtip = msg;
21480                 this.el.dom.qclass = 'x-form-invalid-tip';
21481                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
21482                     Roo.QuickTips.enable();
21483                 }
21484                 break;
21485             case 'title':
21486                 this.el.dom.title = msg;
21487                 break;
21488             case 'under':
21489                 if(!this.errorEl){
21490                     var elp = this.el.findParent('.x-form-element', 5, true);
21491                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
21492                     this.errorEl.setWidth(elp.getWidth(true)-20);
21493                 }
21494                 this.errorEl.update(msg);
21495                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
21496                 break;
21497             case 'side':
21498                 if(!this.errorIcon){
21499                     var elp = this.el.findParent('.x-form-element', 5, true);
21500                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
21501                 }
21502                 this.alignErrorIcon();
21503                 this.errorIcon.dom.qtip = msg;
21504                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
21505                 this.errorIcon.show();
21506                 this.on('resize', this.alignErrorIcon, this);
21507                 break;
21508             default:
21509                 var t = Roo.getDom(this.msgTarget);
21510                 t.innerHTML = msg;
21511                 t.style.display = this.msgDisplay;
21512                 break;
21513         }
21514         this.fireEvent('invalid', this, msg);
21515     },
21516
21517     // private
21518     alignErrorIcon : function(){
21519         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
21520     },
21521
21522     /**
21523      * Clear any invalid styles/messages for this field
21524      */
21525     clearInvalid : function(){
21526         if(!this.rendered || this.preventMark){ // not rendered
21527             return;
21528         }
21529         this.el.removeClass(this.invalidClass);
21530         switch(this.msgTarget){
21531             case 'qtip':
21532                 this.el.dom.qtip = '';
21533                 break;
21534             case 'title':
21535                 this.el.dom.title = '';
21536                 break;
21537             case 'under':
21538                 if(this.errorEl){
21539                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
21540                 }
21541                 break;
21542             case 'side':
21543                 if(this.errorIcon){
21544                     this.errorIcon.dom.qtip = '';
21545                     this.errorIcon.hide();
21546                     this.un('resize', this.alignErrorIcon, this);
21547                 }
21548                 break;
21549             default:
21550                 var t = Roo.getDom(this.msgTarget);
21551                 t.innerHTML = '';
21552                 t.style.display = 'none';
21553                 break;
21554         }
21555         this.fireEvent('valid', this);
21556     },
21557
21558     /**
21559      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
21560      * @return {Mixed} value The field value
21561      */
21562     getRawValue : function(){
21563         var v = this.el.getValue();
21564         if(v === this.emptyText){
21565             v = '';
21566         }
21567         return v;
21568     },
21569
21570     /**
21571      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
21572      * @return {Mixed} value The field value
21573      */
21574     getValue : function(){
21575         var v = this.el.getValue();
21576         if(v === this.emptyText || v === undefined){
21577             v = '';
21578         }
21579         return v;
21580     },
21581
21582     /**
21583      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
21584      * @param {Mixed} value The value to set
21585      */
21586     setRawValue : function(v){
21587         return this.el.dom.value = (v === null || v === undefined ? '' : v);
21588     },
21589
21590     /**
21591      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
21592      * @param {Mixed} value The value to set
21593      */
21594     setValue : function(v){
21595         this.value = v;
21596         if(this.rendered){
21597             this.el.dom.value = (v === null || v === undefined ? '' : v);
21598              this.validate();
21599         }
21600     },
21601
21602     adjustSize : function(w, h){
21603         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21604         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21605         return s;
21606     },
21607
21608     adjustWidth : function(tag, w){
21609         tag = tag.toLowerCase();
21610         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21611             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21612                 if(tag == 'input'){
21613                     return w + 2;
21614                 }
21615                 if(tag = 'textarea'){
21616                     return w-2;
21617                 }
21618             }else if(Roo.isOpera){
21619                 if(tag == 'input'){
21620                     return w + 2;
21621                 }
21622                 if(tag = 'textarea'){
21623                     return w-2;
21624                 }
21625             }
21626         }
21627         return w;
21628     }
21629 });
21630
21631
21632 // anything other than normal should be considered experimental
21633 Roo.form.Field.msgFx = {
21634     normal : {
21635         show: function(msgEl, f){
21636             msgEl.setDisplayed('block');
21637         },
21638
21639         hide : function(msgEl, f){
21640             msgEl.setDisplayed(false).update('');
21641         }
21642     },
21643
21644     slide : {
21645         show: function(msgEl, f){
21646             msgEl.slideIn('t', {stopFx:true});
21647         },
21648
21649         hide : function(msgEl, f){
21650             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21651         }
21652     },
21653
21654     slideRight : {
21655         show: function(msgEl, f){
21656             msgEl.fixDisplay();
21657             msgEl.alignTo(f.el, 'tl-tr');
21658             msgEl.slideIn('l', {stopFx:true});
21659         },
21660
21661         hide : function(msgEl, f){
21662             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21663         }
21664     }
21665 };/*
21666  * Based on:
21667  * Ext JS Library 1.1.1
21668  * Copyright(c) 2006-2007, Ext JS, LLC.
21669  *
21670  * Originally Released Under LGPL - original licence link has changed is not relivant.
21671  *
21672  * Fork - LGPL
21673  * <script type="text/javascript">
21674  */
21675  
21676
21677 /**
21678  * @class Roo.form.TextField
21679  * @extends Roo.form.Field
21680  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
21681  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21682  * @constructor
21683  * Creates a new TextField
21684  * @param {Object} config Configuration options
21685  */
21686 Roo.form.TextField = function(config){
21687     Roo.form.TextField.superclass.constructor.call(this, config);
21688     this.addEvents({
21689         /**
21690          * @event autosize
21691          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
21692          * according to the default logic, but this event provides a hook for the developer to apply additional
21693          * logic at runtime to resize the field if needed.
21694              * @param {Roo.form.Field} this This text field
21695              * @param {Number} width The new field width
21696              */
21697         autosize : true
21698     });
21699 };
21700
21701 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
21702     /**
21703      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21704      */
21705     grow : false,
21706     /**
21707      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21708      */
21709     growMin : 30,
21710     /**
21711      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21712      */
21713     growMax : 800,
21714     /**
21715      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21716      */
21717     vtype : null,
21718     /**
21719      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21720      */
21721     maskRe : null,
21722     /**
21723      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21724      */
21725     disableKeyFilter : false,
21726     /**
21727      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21728      */
21729     allowBlank : true,
21730     /**
21731      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21732      */
21733     minLength : 0,
21734     /**
21735      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21736      */
21737     maxLength : Number.MAX_VALUE,
21738     /**
21739      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21740      */
21741     minLengthText : "The minimum length for this field is {0}",
21742     /**
21743      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21744      */
21745     maxLengthText : "The maximum length for this field is {0}",
21746     /**
21747      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21748      */
21749     selectOnFocus : false,
21750     /**
21751      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21752      */
21753     blankText : "This field is required",
21754     /**
21755      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21756      * If available, this function will be called only after the basic validators all return true, and will be passed the
21757      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21758      */
21759     validator : null,
21760     /**
21761      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21762      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21763      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
21764      */
21765     regex : null,
21766     /**
21767      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21768      */
21769     regexText : "",
21770     /**
21771      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
21772      */
21773     emptyText : null,
21774     /**
21775      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
21776      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
21777      */
21778     emptyClass : 'x-form-empty-field',
21779
21780     // private
21781     initEvents : function(){
21782         Roo.form.TextField.superclass.initEvents.call(this);
21783         if(this.validationEvent == 'keyup'){
21784             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21785             this.el.on('keyup', this.filterValidation, this);
21786         }
21787         else if(this.validationEvent !== false){
21788             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21789         }
21790         if(this.selectOnFocus || this.emptyText){
21791             this.on("focus", this.preFocus, this);
21792             if(this.emptyText){
21793                 this.on('blur', this.postBlur, this);
21794                 this.applyEmptyText();
21795             }
21796         }
21797         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21798             this.el.on("keypress", this.filterKeys, this);
21799         }
21800         if(this.grow){
21801             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
21802             this.el.on("click", this.autoSize,  this);
21803         }
21804     },
21805
21806     processValue : function(value){
21807         if(this.stripCharsRe){
21808             var newValue = value.replace(this.stripCharsRe, '');
21809             if(newValue !== value){
21810                 this.setRawValue(newValue);
21811                 return newValue;
21812             }
21813         }
21814         return value;
21815     },
21816
21817     filterValidation : function(e){
21818         if(!e.isNavKeyPress()){
21819             this.validationTask.delay(this.validationDelay);
21820         }
21821     },
21822
21823     // private
21824     onKeyUp : function(e){
21825         if(!e.isNavKeyPress()){
21826             this.autoSize();
21827         }
21828     },
21829
21830     /**
21831      * Resets the current field value to the originally-loaded value and clears any validation messages.
21832      * Also adds emptyText and emptyClass if the original value was blank.
21833      */
21834     reset : function(){
21835         Roo.form.TextField.superclass.reset.call(this);
21836         this.applyEmptyText();
21837     },
21838
21839     applyEmptyText : function(){
21840         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
21841             this.setRawValue(this.emptyText);
21842             this.el.addClass(this.emptyClass);
21843         }
21844     },
21845
21846     // private
21847     preFocus : function(){
21848         if(this.emptyText){
21849             if(this.el.dom.value == this.emptyText){
21850                 this.setRawValue('');
21851             }
21852             this.el.removeClass(this.emptyClass);
21853         }
21854         if(this.selectOnFocus){
21855             this.el.dom.select();
21856         }
21857     },
21858
21859     // private
21860     postBlur : function(){
21861         this.applyEmptyText();
21862     },
21863
21864     // private
21865     filterKeys : function(e){
21866         var k = e.getKey();
21867         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21868             return;
21869         }
21870         var c = e.getCharCode(), cc = String.fromCharCode(c);
21871         if(Roo.isIE && (e.isSpecialKey() || !cc)){
21872             return;
21873         }
21874         if(!this.maskRe.test(cc)){
21875             e.stopEvent();
21876         }
21877     },
21878
21879     setValue : function(v){
21880         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
21881             this.el.removeClass(this.emptyClass);
21882         }
21883         Roo.form.TextField.superclass.setValue.apply(this, arguments);
21884         this.applyEmptyText();
21885         this.autoSize();
21886     },
21887
21888     /**
21889      * Validates a value according to the field's validation rules and marks the field as invalid
21890      * if the validation fails
21891      * @param {Mixed} value The value to validate
21892      * @return {Boolean} True if the value is valid, else false
21893      */
21894     validateValue : function(value){
21895         if(value.length < 1 || value === this.emptyText){ // if it's blank
21896              if(this.allowBlank){
21897                 this.clearInvalid();
21898                 return true;
21899              }else{
21900                 this.markInvalid(this.blankText);
21901                 return false;
21902              }
21903         }
21904         if(value.length < this.minLength){
21905             this.markInvalid(String.format(this.minLengthText, this.minLength));
21906             return false;
21907         }
21908         if(value.length > this.maxLength){
21909             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
21910             return false;
21911         }
21912         if(this.vtype){
21913             var vt = Roo.form.VTypes;
21914             if(!vt[this.vtype](value, this)){
21915                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
21916                 return false;
21917             }
21918         }
21919         if(typeof this.validator == "function"){
21920             var msg = this.validator(value);
21921             if(msg !== true){
21922                 this.markInvalid(msg);
21923                 return false;
21924             }
21925         }
21926         if(this.regex && !this.regex.test(value)){
21927             this.markInvalid(this.regexText);
21928             return false;
21929         }
21930         return true;
21931     },
21932
21933     /**
21934      * Selects text in this field
21935      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
21936      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
21937      */
21938     selectText : function(start, end){
21939         var v = this.getRawValue();
21940         if(v.length > 0){
21941             start = start === undefined ? 0 : start;
21942             end = end === undefined ? v.length : end;
21943             var d = this.el.dom;
21944             if(d.setSelectionRange){
21945                 d.setSelectionRange(start, end);
21946             }else if(d.createTextRange){
21947                 var range = d.createTextRange();
21948                 range.moveStart("character", start);
21949                 range.moveEnd("character", v.length-end);
21950                 range.select();
21951             }
21952         }
21953     },
21954
21955     /**
21956      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
21957      * This only takes effect if grow = true, and fires the autosize event.
21958      */
21959     autoSize : function(){
21960         if(!this.grow || !this.rendered){
21961             return;
21962         }
21963         if(!this.metrics){
21964             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
21965         }
21966         var el = this.el;
21967         var v = el.dom.value;
21968         var d = document.createElement('div');
21969         d.appendChild(document.createTextNode(v));
21970         v = d.innerHTML;
21971         d = null;
21972         v += "&#160;";
21973         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
21974         this.el.setWidth(w);
21975         this.fireEvent("autosize", this, w);
21976     }
21977 });/*
21978  * Based on:
21979  * Ext JS Library 1.1.1
21980  * Copyright(c) 2006-2007, Ext JS, LLC.
21981  *
21982  * Originally Released Under LGPL - original licence link has changed is not relivant.
21983  *
21984  * Fork - LGPL
21985  * <script type="text/javascript">
21986  */
21987  
21988 /**
21989  * @class Roo.form.Hidden
21990  * @extends Roo.form.TextField
21991  * Simple Hidden element used on forms 
21992  * 
21993  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
21994  * 
21995  * @constructor
21996  * Creates a new Hidden form element.
21997  * @param {Object} config Configuration options
21998  */
21999
22000
22001
22002 // easy hidden field...
22003 Roo.form.Hidden = function(config){
22004     Roo.form.Hidden.superclass.constructor.call(this, config);
22005 };
22006   
22007 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
22008     fieldLabel:      '',
22009     inputType:      'hidden',
22010     width:          50,
22011     allowBlank:     true,
22012     labelSeparator: '',
22013     hidden:         true,
22014     itemCls :       'x-form-item-display-none'
22015
22016
22017 });
22018
22019
22020 /*
22021  * Based on:
22022  * Ext JS Library 1.1.1
22023  * Copyright(c) 2006-2007, Ext JS, LLC.
22024  *
22025  * Originally Released Under LGPL - original licence link has changed is not relivant.
22026  *
22027  * Fork - LGPL
22028  * <script type="text/javascript">
22029  */
22030  
22031 /**
22032  * @class Roo.form.TriggerField
22033  * @extends Roo.form.TextField
22034  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
22035  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
22036  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
22037  * for which you can provide a custom implementation.  For example:
22038  * <pre><code>
22039 var trigger = new Roo.form.TriggerField();
22040 trigger.onTriggerClick = myTriggerFn;
22041 trigger.applyTo('my-field');
22042 </code></pre>
22043  *
22044  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
22045  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
22046  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
22047  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
22048  * @constructor
22049  * Create a new TriggerField.
22050  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
22051  * to the base TextField)
22052  */
22053 Roo.form.TriggerField = function(config){
22054     this.mimicing = false;
22055     Roo.form.TriggerField.superclass.constructor.call(this, config);
22056 };
22057
22058 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
22059     /**
22060      * @cfg {String} triggerClass A CSS class to apply to the trigger
22061      */
22062     /**
22063      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
22064      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
22065      */
22066     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
22067     /**
22068      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
22069      */
22070     hideTrigger:false,
22071
22072     /** @cfg {Boolean} grow @hide */
22073     /** @cfg {Number} growMin @hide */
22074     /** @cfg {Number} growMax @hide */
22075
22076     /**
22077      * @hide 
22078      * @method
22079      */
22080     autoSize: Roo.emptyFn,
22081     // private
22082     monitorTab : true,
22083     // private
22084     deferHeight : true,
22085
22086     
22087     actionMode : 'wrap',
22088     // private
22089     onResize : function(w, h){
22090         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
22091         if(typeof w == 'number'){
22092             var x = w - this.trigger.getWidth();
22093             this.el.setWidth(this.adjustWidth('input', x));
22094             this.trigger.setStyle('left', x+'px');
22095         }
22096     },
22097
22098     // private
22099     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22100
22101     // private
22102     getResizeEl : function(){
22103         return this.wrap;
22104     },
22105
22106     // private
22107     getPositionEl : function(){
22108         return this.wrap;
22109     },
22110
22111     // private
22112     alignErrorIcon : function(){
22113         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
22114     },
22115
22116     // private
22117     onRender : function(ct, position){
22118         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
22119         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
22120         this.trigger = this.wrap.createChild(this.triggerConfig ||
22121                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
22122         if(this.hideTrigger){
22123             this.trigger.setDisplayed(false);
22124         }
22125         this.initTrigger();
22126         if(!this.width){
22127             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
22128         }
22129     },
22130
22131     // private
22132     initTrigger : function(){
22133         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
22134         this.trigger.addClassOnOver('x-form-trigger-over');
22135         this.trigger.addClassOnClick('x-form-trigger-click');
22136     },
22137
22138     // private
22139     onDestroy : function(){
22140         if(this.trigger){
22141             this.trigger.removeAllListeners();
22142             this.trigger.remove();
22143         }
22144         if(this.wrap){
22145             this.wrap.remove();
22146         }
22147         Roo.form.TriggerField.superclass.onDestroy.call(this);
22148     },
22149
22150     // private
22151     onFocus : function(){
22152         Roo.form.TriggerField.superclass.onFocus.call(this);
22153         if(!this.mimicing){
22154             this.wrap.addClass('x-trigger-wrap-focus');
22155             this.mimicing = true;
22156             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
22157             if(this.monitorTab){
22158                 this.el.on("keydown", this.checkTab, this);
22159             }
22160         }
22161     },
22162
22163     // private
22164     checkTab : function(e){
22165         if(e.getKey() == e.TAB){
22166             this.triggerBlur();
22167         }
22168     },
22169
22170     // private
22171     onBlur : function(){
22172         // do nothing
22173     },
22174
22175     // private
22176     mimicBlur : function(e, t){
22177         if(!this.wrap.contains(t) && this.validateBlur()){
22178             this.triggerBlur();
22179         }
22180     },
22181
22182     // private
22183     triggerBlur : function(){
22184         this.mimicing = false;
22185         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
22186         if(this.monitorTab){
22187             this.el.un("keydown", this.checkTab, this);
22188         }
22189         this.wrap.removeClass('x-trigger-wrap-focus');
22190         Roo.form.TriggerField.superclass.onBlur.call(this);
22191     },
22192
22193     // private
22194     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
22195     validateBlur : function(e, t){
22196         return true;
22197     },
22198
22199     // private
22200     onDisable : function(){
22201         Roo.form.TriggerField.superclass.onDisable.call(this);
22202         if(this.wrap){
22203             this.wrap.addClass('x-item-disabled');
22204         }
22205     },
22206
22207     // private
22208     onEnable : function(){
22209         Roo.form.TriggerField.superclass.onEnable.call(this);
22210         if(this.wrap){
22211             this.wrap.removeClass('x-item-disabled');
22212         }
22213     },
22214
22215     // private
22216     onShow : function(){
22217         var ae = this.getActionEl();
22218         
22219         if(ae){
22220             ae.dom.style.display = '';
22221             ae.dom.style.visibility = 'visible';
22222         }
22223     },
22224
22225     // private
22226     
22227     onHide : function(){
22228         var ae = this.getActionEl();
22229         ae.dom.style.display = 'none';
22230     },
22231
22232     /**
22233      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
22234      * by an implementing function.
22235      * @method
22236      * @param {EventObject} e
22237      */
22238     onTriggerClick : Roo.emptyFn
22239 });
22240
22241 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
22242 // to be extended by an implementing class.  For an example of implementing this class, see the custom
22243 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
22244 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
22245     initComponent : function(){
22246         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
22247
22248         this.triggerConfig = {
22249             tag:'span', cls:'x-form-twin-triggers', cn:[
22250             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
22251             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
22252         ]};
22253     },
22254
22255     getTrigger : function(index){
22256         return this.triggers[index];
22257     },
22258
22259     initTrigger : function(){
22260         var ts = this.trigger.select('.x-form-trigger', true);
22261         this.wrap.setStyle('overflow', 'hidden');
22262         var triggerField = this;
22263         ts.each(function(t, all, index){
22264             t.hide = function(){
22265                 var w = triggerField.wrap.getWidth();
22266                 this.dom.style.display = 'none';
22267                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
22268             };
22269             t.show = function(){
22270                 var w = triggerField.wrap.getWidth();
22271                 this.dom.style.display = '';
22272                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
22273             };
22274             var triggerIndex = 'Trigger'+(index+1);
22275
22276             if(this['hide'+triggerIndex]){
22277                 t.dom.style.display = 'none';
22278             }
22279             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
22280             t.addClassOnOver('x-form-trigger-over');
22281             t.addClassOnClick('x-form-trigger-click');
22282         }, this);
22283         this.triggers = ts.elements;
22284     },
22285
22286     onTrigger1Click : Roo.emptyFn,
22287     onTrigger2Click : Roo.emptyFn
22288 });/*
22289  * Based on:
22290  * Ext JS Library 1.1.1
22291  * Copyright(c) 2006-2007, Ext JS, LLC.
22292  *
22293  * Originally Released Under LGPL - original licence link has changed is not relivant.
22294  *
22295  * Fork - LGPL
22296  * <script type="text/javascript">
22297  */
22298  
22299 /**
22300  * @class Roo.form.TextArea
22301  * @extends Roo.form.TextField
22302  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
22303  * support for auto-sizing.
22304  * @constructor
22305  * Creates a new TextArea
22306  * @param {Object} config Configuration options
22307  */
22308 Roo.form.TextArea = function(config){
22309     Roo.form.TextArea.superclass.constructor.call(this, config);
22310     // these are provided exchanges for backwards compat
22311     // minHeight/maxHeight were replaced by growMin/growMax to be
22312     // compatible with TextField growing config values
22313     if(this.minHeight !== undefined){
22314         this.growMin = this.minHeight;
22315     }
22316     if(this.maxHeight !== undefined){
22317         this.growMax = this.maxHeight;
22318     }
22319 };
22320
22321 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
22322     /**
22323      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
22324      */
22325     growMin : 60,
22326     /**
22327      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
22328      */
22329     growMax: 1000,
22330     /**
22331      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
22332      * in the field (equivalent to setting overflow: hidden, defaults to false)
22333      */
22334     preventScrollbars: false,
22335     /**
22336      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
22337      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
22338      */
22339
22340     // private
22341     onRender : function(ct, position){
22342         if(!this.el){
22343             this.defaultAutoCreate = {
22344                 tag: "textarea",
22345                 style:"width:300px;height:60px;",
22346                 autocomplete: "off"
22347             };
22348         }
22349         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
22350         if(this.grow){
22351             this.textSizeEl = Roo.DomHelper.append(document.body, {
22352                 tag: "pre", cls: "x-form-grow-sizer"
22353             });
22354             if(this.preventScrollbars){
22355                 this.el.setStyle("overflow", "hidden");
22356             }
22357             this.el.setHeight(this.growMin);
22358         }
22359     },
22360
22361     onDestroy : function(){
22362         if(this.textSizeEl){
22363             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
22364         }
22365         Roo.form.TextArea.superclass.onDestroy.call(this);
22366     },
22367
22368     // private
22369     onKeyUp : function(e){
22370         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
22371             this.autoSize();
22372         }
22373     },
22374
22375     /**
22376      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
22377      * This only takes effect if grow = true, and fires the autosize event if the height changes.
22378      */
22379     autoSize : function(){
22380         if(!this.grow || !this.textSizeEl){
22381             return;
22382         }
22383         var el = this.el;
22384         var v = el.dom.value;
22385         var ts = this.textSizeEl;
22386
22387         ts.innerHTML = '';
22388         ts.appendChild(document.createTextNode(v));
22389         v = ts.innerHTML;
22390
22391         Roo.fly(ts).setWidth(this.el.getWidth());
22392         if(v.length < 1){
22393             v = "&#160;&#160;";
22394         }else{
22395             if(Roo.isIE){
22396                 v = v.replace(/\n/g, '<p>&#160;</p>');
22397             }
22398             v += "&#160;\n&#160;";
22399         }
22400         ts.innerHTML = v;
22401         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
22402         if(h != this.lastHeight){
22403             this.lastHeight = h;
22404             this.el.setHeight(h);
22405             this.fireEvent("autosize", this, h);
22406         }
22407     }
22408 });/*
22409  * Based on:
22410  * Ext JS Library 1.1.1
22411  * Copyright(c) 2006-2007, Ext JS, LLC.
22412  *
22413  * Originally Released Under LGPL - original licence link has changed is not relivant.
22414  *
22415  * Fork - LGPL
22416  * <script type="text/javascript">
22417  */
22418  
22419
22420 /**
22421  * @class Roo.form.NumberField
22422  * @extends Roo.form.TextField
22423  * Numeric text field that provides automatic keystroke filtering and numeric validation.
22424  * @constructor
22425  * Creates a new NumberField
22426  * @param {Object} config Configuration options
22427  */
22428 Roo.form.NumberField = function(config){
22429     Roo.form.NumberField.superclass.constructor.call(this, config);
22430 };
22431
22432 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
22433     /**
22434      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
22435      */
22436     fieldClass: "x-form-field x-form-num-field",
22437     /**
22438      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
22439      */
22440     allowDecimals : true,
22441     /**
22442      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
22443      */
22444     decimalSeparator : ".",
22445     /**
22446      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
22447      */
22448     decimalPrecision : 2,
22449     /**
22450      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
22451      */
22452     allowNegative : true,
22453     /**
22454      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
22455      */
22456     minValue : Number.NEGATIVE_INFINITY,
22457     /**
22458      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
22459      */
22460     maxValue : Number.MAX_VALUE,
22461     /**
22462      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
22463      */
22464     minText : "The minimum value for this field is {0}",
22465     /**
22466      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
22467      */
22468     maxText : "The maximum value for this field is {0}",
22469     /**
22470      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
22471      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
22472      */
22473     nanText : "{0} is not a valid number",
22474
22475     // private
22476     initEvents : function(){
22477         Roo.form.NumberField.superclass.initEvents.call(this);
22478         var allowed = "0123456789";
22479         if(this.allowDecimals){
22480             allowed += this.decimalSeparator;
22481         }
22482         if(this.allowNegative){
22483             allowed += "-";
22484         }
22485         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
22486         var keyPress = function(e){
22487             var k = e.getKey();
22488             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
22489                 return;
22490             }
22491             var c = e.getCharCode();
22492             if(allowed.indexOf(String.fromCharCode(c)) === -1){
22493                 e.stopEvent();
22494             }
22495         };
22496         this.el.on("keypress", keyPress, this);
22497     },
22498
22499     // private
22500     validateValue : function(value){
22501         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
22502             return false;
22503         }
22504         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22505              return true;
22506         }
22507         var num = this.parseValue(value);
22508         if(isNaN(num)){
22509             this.markInvalid(String.format(this.nanText, value));
22510             return false;
22511         }
22512         if(num < this.minValue){
22513             this.markInvalid(String.format(this.minText, this.minValue));
22514             return false;
22515         }
22516         if(num > this.maxValue){
22517             this.markInvalid(String.format(this.maxText, this.maxValue));
22518             return false;
22519         }
22520         return true;
22521     },
22522
22523     getValue : function(){
22524         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
22525     },
22526
22527     // private
22528     parseValue : function(value){
22529         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
22530         return isNaN(value) ? '' : value;
22531     },
22532
22533     // private
22534     fixPrecision : function(value){
22535         var nan = isNaN(value);
22536         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
22537             return nan ? '' : value;
22538         }
22539         return parseFloat(value).toFixed(this.decimalPrecision);
22540     },
22541
22542     setValue : function(v){
22543         v = this.fixPrecision(v);
22544         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
22545     },
22546
22547     // private
22548     decimalPrecisionFcn : function(v){
22549         return Math.floor(v);
22550     },
22551
22552     beforeBlur : function(){
22553         var v = this.parseValue(this.getRawValue());
22554         if(v){
22555             this.setValue(v);
22556         }
22557     }
22558 });/*
22559  * Based on:
22560  * Ext JS Library 1.1.1
22561  * Copyright(c) 2006-2007, Ext JS, LLC.
22562  *
22563  * Originally Released Under LGPL - original licence link has changed is not relivant.
22564  *
22565  * Fork - LGPL
22566  * <script type="text/javascript">
22567  */
22568  
22569 /**
22570  * @class Roo.form.DateField
22571  * @extends Roo.form.TriggerField
22572  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22573 * @constructor
22574 * Create a new DateField
22575 * @param {Object} config
22576  */
22577 Roo.form.DateField = function(config){
22578     Roo.form.DateField.superclass.constructor.call(this, config);
22579     
22580       this.addEvents({
22581          
22582         /**
22583          * @event select
22584          * Fires when a date is selected
22585              * @param {Roo.form.DateField} combo This combo box
22586              * @param {Date} date The date selected
22587              */
22588         'select' : true
22589          
22590     });
22591     
22592     
22593     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22594     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22595     this.ddMatch = null;
22596     if(this.disabledDates){
22597         var dd = this.disabledDates;
22598         var re = "(?:";
22599         for(var i = 0; i < dd.length; i++){
22600             re += dd[i];
22601             if(i != dd.length-1) re += "|";
22602         }
22603         this.ddMatch = new RegExp(re + ")");
22604     }
22605 };
22606
22607 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
22608     /**
22609      * @cfg {String} format
22610      * The default date format string which can be overriden for localization support.  The format must be
22611      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22612      */
22613     format : "m/d/y",
22614     /**
22615      * @cfg {String} altFormats
22616      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22617      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22618      */
22619     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22620     /**
22621      * @cfg {Array} disabledDays
22622      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22623      */
22624     disabledDays : null,
22625     /**
22626      * @cfg {String} disabledDaysText
22627      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22628      */
22629     disabledDaysText : "Disabled",
22630     /**
22631      * @cfg {Array} disabledDates
22632      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22633      * expression so they are very powerful. Some examples:
22634      * <ul>
22635      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22636      * <li>["03/08", "09/16"] would disable those days for every year</li>
22637      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22638      * <li>["03/../2006"] would disable every day in March 2006</li>
22639      * <li>["^03"] would disable every day in every March</li>
22640      * </ul>
22641      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22642      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22643      */
22644     disabledDates : null,
22645     /**
22646      * @cfg {String} disabledDatesText
22647      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22648      */
22649     disabledDatesText : "Disabled",
22650     /**
22651      * @cfg {Date/String} minValue
22652      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22653      * valid format (defaults to null).
22654      */
22655     minValue : null,
22656     /**
22657      * @cfg {Date/String} maxValue
22658      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22659      * valid format (defaults to null).
22660      */
22661     maxValue : null,
22662     /**
22663      * @cfg {String} minText
22664      * The error text to display when the date in the cell is before minValue (defaults to
22665      * 'The date in this field must be after {minValue}').
22666      */
22667     minText : "The date in this field must be equal to or after {0}",
22668     /**
22669      * @cfg {String} maxText
22670      * The error text to display when the date in the cell is after maxValue (defaults to
22671      * 'The date in this field must be before {maxValue}').
22672      */
22673     maxText : "The date in this field must be equal to or before {0}",
22674     /**
22675      * @cfg {String} invalidText
22676      * The error text to display when the date in the field is invalid (defaults to
22677      * '{value} is not a valid date - it must be in the format {format}').
22678      */
22679     invalidText : "{0} is not a valid date - it must be in the format {1}",
22680     /**
22681      * @cfg {String} triggerClass
22682      * An additional CSS class used to style the trigger button.  The trigger will always get the
22683      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22684      * which displays a calendar icon).
22685      */
22686     triggerClass : 'x-form-date-trigger',
22687     
22688
22689     /**
22690      * @cfg {Boolean} useIso
22691      * if enabled, then the date field will use a hidden field to store the 
22692      * real value as iso formated date. default (false)
22693      */ 
22694     useIso : false,
22695     /**
22696      * @cfg {String/Object} autoCreate
22697      * A DomHelper element spec, or true for a default element spec (defaults to
22698      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22699      */ 
22700     // private
22701     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22702     
22703     // private
22704     hiddenField: false,
22705     
22706     onRender : function(ct, position)
22707     {
22708         Roo.form.DateField.superclass.onRender.call(this, ct, position);
22709         if (this.useIso) {
22710             this.el.dom.removeAttribute('name'); 
22711             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22712                     'before', true);
22713             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
22714             // prevent input submission
22715             this.hiddenName = this.name;
22716         }
22717             
22718             
22719     },
22720     
22721     // private
22722     validateValue : function(value)
22723     {
22724         value = this.formatDate(value);
22725         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22726             return false;
22727         }
22728         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22729              return true;
22730         }
22731         var svalue = value;
22732         value = this.parseDate(value);
22733         if(!value){
22734             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22735             return false;
22736         }
22737         var time = value.getTime();
22738         if(this.minValue && time < this.minValue.getTime()){
22739             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22740             return false;
22741         }
22742         if(this.maxValue && time > this.maxValue.getTime()){
22743             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22744             return false;
22745         }
22746         if(this.disabledDays){
22747             var day = value.getDay();
22748             for(var i = 0; i < this.disabledDays.length; i++) {
22749                 if(day === this.disabledDays[i]){
22750                     this.markInvalid(this.disabledDaysText);
22751                     return false;
22752                 }
22753             }
22754         }
22755         var fvalue = this.formatDate(value);
22756         if(this.ddMatch && this.ddMatch.test(fvalue)){
22757             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22758             return false;
22759         }
22760         return true;
22761     },
22762
22763     // private
22764     // Provides logic to override the default TriggerField.validateBlur which just returns true
22765     validateBlur : function(){
22766         return !this.menu || !this.menu.isVisible();
22767     },
22768
22769     /**
22770      * Returns the current date value of the date field.
22771      * @return {Date} The date value
22772      */
22773     getValue : function(){
22774         
22775         return  this.hiddenField ?
22776                 this.hiddenField.value :
22777                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22778     },
22779
22780     /**
22781      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22782      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22783      * (the default format used is "m/d/y").
22784      * <br />Usage:
22785      * <pre><code>
22786 //All of these calls set the same date value (May 4, 2006)
22787
22788 //Pass a date object:
22789 var dt = new Date('5/4/06');
22790 dateField.setValue(dt);
22791
22792 //Pass a date string (default format):
22793 dateField.setValue('5/4/06');
22794
22795 //Pass a date string (custom format):
22796 dateField.format = 'Y-m-d';
22797 dateField.setValue('2006-5-4');
22798 </code></pre>
22799      * @param {String/Date} date The date or valid date string
22800      */
22801     setValue : function(date){
22802         if (this.hiddenField) {
22803             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22804         }
22805         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22806     },
22807
22808     // private
22809     parseDate : function(value){
22810         if(!value || value instanceof Date){
22811             return value;
22812         }
22813         var v = Date.parseDate(value, this.format);
22814          if (this.useIso) {
22815             v = Date.parseDate(value, 'Y-m-d');
22816         }
22817         if(!v && this.altFormats){
22818             if(!this.altFormatsArray){
22819                 this.altFormatsArray = this.altFormats.split("|");
22820             }
22821             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22822                 v = Date.parseDate(value, this.altFormatsArray[i]);
22823             }
22824         }
22825         return v;
22826     },
22827
22828     // private
22829     formatDate : function(date, fmt){
22830         return (!date || !(date instanceof Date)) ?
22831                date : date.dateFormat(fmt || this.format);
22832     },
22833
22834     // private
22835     menuListeners : {
22836         select: function(m, d){
22837             this.setValue(d);
22838             this.fireEvent('select', this, d);
22839         },
22840         show : function(){ // retain focus styling
22841             this.onFocus();
22842         },
22843         hide : function(){
22844             this.focus.defer(10, this);
22845             var ml = this.menuListeners;
22846             this.menu.un("select", ml.select,  this);
22847             this.menu.un("show", ml.show,  this);
22848             this.menu.un("hide", ml.hide,  this);
22849         }
22850     },
22851
22852     // private
22853     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22854     onTriggerClick : function(){
22855         if(this.disabled){
22856             return;
22857         }
22858         if(this.menu == null){
22859             this.menu = new Roo.menu.DateMenu();
22860         }
22861         Roo.apply(this.menu.picker,  {
22862             showClear: this.allowBlank,
22863             minDate : this.minValue,
22864             maxDate : this.maxValue,
22865             disabledDatesRE : this.ddMatch,
22866             disabledDatesText : this.disabledDatesText,
22867             disabledDays : this.disabledDays,
22868             disabledDaysText : this.disabledDaysText,
22869             format : this.useIso ? 'Y-m-d' : this.format,
22870             minText : String.format(this.minText, this.formatDate(this.minValue)),
22871             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22872         });
22873         this.menu.on(Roo.apply({}, this.menuListeners, {
22874             scope:this
22875         }));
22876         this.menu.picker.setValue(this.getValue() || new Date());
22877         this.menu.show(this.el, "tl-bl?");
22878     },
22879
22880     beforeBlur : function(){
22881         var v = this.parseDate(this.getRawValue());
22882         if(v){
22883             this.setValue(v);
22884         }
22885     }
22886
22887     /** @cfg {Boolean} grow @hide */
22888     /** @cfg {Number} growMin @hide */
22889     /** @cfg {Number} growMax @hide */
22890     /**
22891      * @hide
22892      * @method autoSize
22893      */
22894 });/*
22895  * Based on:
22896  * Ext JS Library 1.1.1
22897  * Copyright(c) 2006-2007, Ext JS, LLC.
22898  *
22899  * Originally Released Under LGPL - original licence link has changed is not relivant.
22900  *
22901  * Fork - LGPL
22902  * <script type="text/javascript">
22903  */
22904  
22905 /**
22906  * @class Roo.form.MonthField
22907  * @extends Roo.form.TriggerField
22908  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22909 * @constructor
22910 * Create a new MonthField
22911 * @param {Object} config
22912  */
22913 Roo.form.MonthField = function(config){
22914     
22915     Roo.form.MonthField.superclass.constructor.call(this, config);
22916     
22917       this.addEvents({
22918          
22919         /**
22920          * @event select
22921          * Fires when a date is selected
22922              * @param {Roo.form.MonthFieeld} combo This combo box
22923              * @param {Date} date The date selected
22924              */
22925         'select' : true
22926          
22927     });
22928     
22929     
22930     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22931     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22932     this.ddMatch = null;
22933     if(this.disabledDates){
22934         var dd = this.disabledDates;
22935         var re = "(?:";
22936         for(var i = 0; i < dd.length; i++){
22937             re += dd[i];
22938             if(i != dd.length-1) re += "|";
22939         }
22940         this.ddMatch = new RegExp(re + ")");
22941     }
22942 };
22943
22944 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
22945     /**
22946      * @cfg {String} format
22947      * The default date format string which can be overriden for localization support.  The format must be
22948      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22949      */
22950     format : "M Y",
22951     /**
22952      * @cfg {String} altFormats
22953      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22954      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22955      */
22956     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
22957     /**
22958      * @cfg {Array} disabledDays
22959      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22960      */
22961     disabledDays : [0,1,2,3,4,5,6],
22962     /**
22963      * @cfg {String} disabledDaysText
22964      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22965      */
22966     disabledDaysText : "Disabled",
22967     /**
22968      * @cfg {Array} disabledDates
22969      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22970      * expression so they are very powerful. Some examples:
22971      * <ul>
22972      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22973      * <li>["03/08", "09/16"] would disable those days for every year</li>
22974      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22975      * <li>["03/../2006"] would disable every day in March 2006</li>
22976      * <li>["^03"] would disable every day in every March</li>
22977      * </ul>
22978      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22979      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22980      */
22981     disabledDates : null,
22982     /**
22983      * @cfg {String} disabledDatesText
22984      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22985      */
22986     disabledDatesText : "Disabled",
22987     /**
22988      * @cfg {Date/String} minValue
22989      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22990      * valid format (defaults to null).
22991      */
22992     minValue : null,
22993     /**
22994      * @cfg {Date/String} maxValue
22995      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22996      * valid format (defaults to null).
22997      */
22998     maxValue : null,
22999     /**
23000      * @cfg {String} minText
23001      * The error text to display when the date in the cell is before minValue (defaults to
23002      * 'The date in this field must be after {minValue}').
23003      */
23004     minText : "The date in this field must be equal to or after {0}",
23005     /**
23006      * @cfg {String} maxTextf
23007      * The error text to display when the date in the cell is after maxValue (defaults to
23008      * 'The date in this field must be before {maxValue}').
23009      */
23010     maxText : "The date in this field must be equal to or before {0}",
23011     /**
23012      * @cfg {String} invalidText
23013      * The error text to display when the date in the field is invalid (defaults to
23014      * '{value} is not a valid date - it must be in the format {format}').
23015      */
23016     invalidText : "{0} is not a valid date - it must be in the format {1}",
23017     /**
23018      * @cfg {String} triggerClass
23019      * An additional CSS class used to style the trigger button.  The trigger will always get the
23020      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
23021      * which displays a calendar icon).
23022      */
23023     triggerClass : 'x-form-date-trigger',
23024     
23025
23026     /**
23027      * @cfg {Boolean} useIso
23028      * if enabled, then the date field will use a hidden field to store the 
23029      * real value as iso formated date. default (true)
23030      */ 
23031     useIso : true,
23032     /**
23033      * @cfg {String/Object} autoCreate
23034      * A DomHelper element spec, or true for a default element spec (defaults to
23035      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
23036      */ 
23037     // private
23038     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
23039     
23040     // private
23041     hiddenField: false,
23042     
23043     hideMonthPicker : false,
23044     
23045     onRender : function(ct, position)
23046     {
23047         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
23048         if (this.useIso) {
23049             this.el.dom.removeAttribute('name'); 
23050             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
23051                     'before', true);
23052             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
23053             // prevent input submission
23054             this.hiddenName = this.name;
23055         }
23056             
23057             
23058     },
23059     
23060     // private
23061     validateValue : function(value)
23062     {
23063         value = this.formatDate(value);
23064         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
23065             return false;
23066         }
23067         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
23068              return true;
23069         }
23070         var svalue = value;
23071         value = this.parseDate(value);
23072         if(!value){
23073             this.markInvalid(String.format(this.invalidText, svalue, this.format));
23074             return false;
23075         }
23076         var time = value.getTime();
23077         if(this.minValue && time < this.minValue.getTime()){
23078             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
23079             return false;
23080         }
23081         if(this.maxValue && time > this.maxValue.getTime()){
23082             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
23083             return false;
23084         }
23085         /*if(this.disabledDays){
23086             var day = value.getDay();
23087             for(var i = 0; i < this.disabledDays.length; i++) {
23088                 if(day === this.disabledDays[i]){
23089                     this.markInvalid(this.disabledDaysText);
23090                     return false;
23091                 }
23092             }
23093         }
23094         */
23095         var fvalue = this.formatDate(value);
23096         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
23097             this.markInvalid(String.format(this.disabledDatesText, fvalue));
23098             return false;
23099         }
23100         */
23101         return true;
23102     },
23103
23104     // private
23105     // Provides logic to override the default TriggerField.validateBlur which just returns true
23106     validateBlur : function(){
23107         return !this.menu || !this.menu.isVisible();
23108     },
23109
23110     /**
23111      * Returns the current date value of the date field.
23112      * @return {Date} The date value
23113      */
23114     getValue : function(){
23115         
23116         
23117         
23118         return  this.hiddenField ?
23119                 this.hiddenField.value :
23120                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
23121     },
23122
23123     /**
23124      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
23125      * date, using monthField.format as the date format, according to the same rules as {@link Date#parseDate}
23126      * (the default format used is "m/d/y").
23127      * <br />Usage:
23128      * <pre><code>
23129 //All of these calls set the same date value (May 4, 2006)
23130
23131 //Pass a date object:
23132 var dt = new Date('5/4/06');
23133 monthField.setValue(dt);
23134
23135 //Pass a date string (default format):
23136 monthField.setValue('5/4/06');
23137
23138 //Pass a date string (custom format):
23139 monthField.format = 'Y-m-d';
23140 monthField.setValue('2006-5-4');
23141 </code></pre>
23142      * @param {String/Date} date The date or valid date string
23143      */
23144     setValue : function(date){
23145         Roo.log('month setValue' + date);
23146         if (this.hiddenField) {
23147             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
23148         }
23149         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
23150     },
23151
23152     // private
23153     parseDate : function(value){
23154         if(!value || value instanceof Date){
23155             return value;
23156         }
23157         var v = Date.parseDate(value, this.format);
23158         if (this.useIso) {
23159             v = Date.parseDate(value, 'Y-m-d');
23160         }
23161         
23162         
23163         if(!v && this.altFormats){
23164             if(!this.altFormatsArray){
23165                 this.altFormatsArray = this.altFormats.split("|");
23166             }
23167             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23168                 v = Date.parseDate(value, this.altFormatsArray[i]);
23169             }
23170         }
23171         return v;
23172     },
23173
23174     // private
23175     formatDate : function(date, fmt){
23176         return (!date || !(date instanceof Date)) ?
23177                date : date.dateFormat(fmt || this.format);
23178     },
23179
23180     // private
23181     menuListeners : {
23182         select: function(m, d){
23183             this.setValue(d);
23184             this.fireEvent('select', this, d);
23185         },
23186         show : function(){ // retain focus styling
23187             this.onFocus();
23188         },
23189         hide : function(){
23190             this.focus.defer(10, this);
23191             var ml = this.menuListeners;
23192             this.menu.un("select", ml.select,  this);
23193             this.menu.un("show", ml.show,  this);
23194             this.menu.un("hide", ml.hide,  this);
23195         }
23196     },
23197     // private
23198     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
23199     onTriggerClick : function(){
23200         if(this.disabled){
23201             return;
23202         }
23203         if(this.menu == null){
23204             this.menu = new Roo.menu.DateMenu();
23205         }
23206         
23207         Roo.apply(this.menu.picker,  {
23208             
23209             showClear: this.allowBlank,
23210             minDate : this.minValue,
23211             maxDate : this.maxValue,
23212             disabledDatesRE : this.ddMatch,
23213             disabledDatesText : this.disabledDatesText,
23214             
23215             format : this.format,
23216             minText : String.format(this.minText, this.formatDate(this.minValue)),
23217             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
23218             
23219         });
23220         
23221         this.menu.on(Roo.apply({}, this.menuListeners, {
23222             scope:this
23223         }));
23224         
23225         var m = this.menu;
23226         var p = m.picker;
23227         p.format = this.useIso ? 'Y-m-d' : this.format;  // make sure they are the same..?
23228         Roo.log('picker set value');
23229         Roo.log(this.getValue());
23230         p.setValue(this.getValue() || new Date());
23231         m.show(this.el, "tl-bl?");
23232         
23233         // hidden the day picker
23234         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
23235         
23236         (function() {
23237             p.showMonthPicker();
23238         }).defer(100);
23239         
23240         p.hideMonthPicker  = function(disableAnim){
23241             if(this.monthPicker){
23242                 if(disableAnim === true){
23243                     this.monthPicker.hide();
23244                 }else{
23245                     this.monthPicker.slideOut('t', {duration:.2});
23246                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth));
23247                     p.fireEvent("select", this, this.value);
23248                     m.hide();
23249                 }
23250             }
23251         }
23252     },
23253
23254     beforeBlur : function(){
23255         var v = this.parseDate(this.getRawValue());
23256         if(v){
23257             this.setValue(v);
23258         }
23259     }
23260
23261     /** @cfg {Boolean} grow @hide */
23262     /** @cfg {Number} growMin @hide */
23263     /** @cfg {Number} growMax @hide */
23264     /**
23265      * @hide
23266      * @method autoSize
23267      */
23268 });/*
23269  * Based on:
23270  * Ext JS Library 1.1.1
23271  * Copyright(c) 2006-2007, Ext JS, LLC.
23272  *
23273  * Originally Released Under LGPL - original licence link has changed is not relivant.
23274  *
23275  * Fork - LGPL
23276  * <script type="text/javascript">
23277  */
23278  
23279
23280 /**
23281  * @class Roo.form.ComboBox
23282  * @extends Roo.form.TriggerField
23283  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
23284  * @constructor
23285  * Create a new ComboBox.
23286  * @param {Object} config Configuration options
23287  */
23288 Roo.form.ComboBox = function(config){
23289     Roo.form.ComboBox.superclass.constructor.call(this, config);
23290     this.addEvents({
23291         /**
23292          * @event expand
23293          * Fires when the dropdown list is expanded
23294              * @param {Roo.form.ComboBox} combo This combo box
23295              */
23296         'expand' : true,
23297         /**
23298          * @event collapse
23299          * Fires when the dropdown list is collapsed
23300              * @param {Roo.form.ComboBox} combo This combo box
23301              */
23302         'collapse' : true,
23303         /**
23304          * @event beforeselect
23305          * Fires before a list item is selected. Return false to cancel the selection.
23306              * @param {Roo.form.ComboBox} combo This combo box
23307              * @param {Roo.data.Record} record The data record returned from the underlying store
23308              * @param {Number} index The index of the selected item in the dropdown list
23309              */
23310         'beforeselect' : true,
23311         /**
23312          * @event select
23313          * Fires when a list item is selected
23314              * @param {Roo.form.ComboBox} combo This combo box
23315              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
23316              * @param {Number} index The index of the selected item in the dropdown list
23317              */
23318         'select' : true,
23319         /**
23320          * @event beforequery
23321          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
23322          * The event object passed has these properties:
23323              * @param {Roo.form.ComboBox} combo This combo box
23324              * @param {String} query The query
23325              * @param {Boolean} forceAll true to force "all" query
23326              * @param {Boolean} cancel true to cancel the query
23327              * @param {Object} e The query event object
23328              */
23329         'beforequery': true,
23330          /**
23331          * @event add
23332          * Fires when the 'add' icon is pressed (add a listener to enable add button)
23333              * @param {Roo.form.ComboBox} combo This combo box
23334              */
23335         'add' : true,
23336         /**
23337          * @event edit
23338          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
23339              * @param {Roo.form.ComboBox} combo This combo box
23340              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
23341              */
23342         'edit' : true
23343         
23344         
23345     });
23346     if(this.transform){
23347         this.allowDomMove = false;
23348         var s = Roo.getDom(this.transform);
23349         if(!this.hiddenName){
23350             this.hiddenName = s.name;
23351         }
23352         if(!this.store){
23353             this.mode = 'local';
23354             var d = [], opts = s.options;
23355             for(var i = 0, len = opts.length;i < len; i++){
23356                 var o = opts[i];
23357                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
23358                 if(o.selected) {
23359                     this.value = value;
23360                 }
23361                 d.push([value, o.text]);
23362             }
23363             this.store = new Roo.data.SimpleStore({
23364                 'id': 0,
23365                 fields: ['value', 'text'],
23366                 data : d
23367             });
23368             this.valueField = 'value';
23369             this.displayField = 'text';
23370         }
23371         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
23372         if(!this.lazyRender){
23373             this.target = true;
23374             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
23375             s.parentNode.removeChild(s); // remove it
23376             this.render(this.el.parentNode);
23377         }else{
23378             s.parentNode.removeChild(s); // remove it
23379         }
23380
23381     }
23382     if (this.store) {
23383         this.store = Roo.factory(this.store, Roo.data);
23384     }
23385     
23386     this.selectedIndex = -1;
23387     if(this.mode == 'local'){
23388         if(config.queryDelay === undefined){
23389             this.queryDelay = 10;
23390         }
23391         if(config.minChars === undefined){
23392             this.minChars = 0;
23393         }
23394     }
23395 };
23396
23397 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
23398     /**
23399      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
23400      */
23401     /**
23402      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
23403      * rendering into an Roo.Editor, defaults to false)
23404      */
23405     /**
23406      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
23407      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
23408      */
23409     /**
23410      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
23411      */
23412     /**
23413      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
23414      * the dropdown list (defaults to undefined, with no header element)
23415      */
23416
23417      /**
23418      * @cfg {String/Roo.Template} tpl The template to use to render the output
23419      */
23420      
23421     // private
23422     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
23423     /**
23424      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
23425      */
23426     listWidth: undefined,
23427     /**
23428      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
23429      * mode = 'remote' or 'text' if mode = 'local')
23430      */
23431     displayField: undefined,
23432     /**
23433      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
23434      * mode = 'remote' or 'value' if mode = 'local'). 
23435      * Note: use of a valueField requires the user make a selection
23436      * in order for a value to be mapped.
23437      */
23438     valueField: undefined,
23439     
23440     
23441     /**
23442      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
23443      * field's data value (defaults to the underlying DOM element's name)
23444      */
23445     hiddenName: undefined,
23446     /**
23447      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
23448      */
23449     listClass: '',
23450     /**
23451      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
23452      */
23453     selectedClass: 'x-combo-selected',
23454     /**
23455      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
23456      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
23457      * which displays a downward arrow icon).
23458      */
23459     triggerClass : 'x-form-arrow-trigger',
23460     /**
23461      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
23462      */
23463     shadow:'sides',
23464     /**
23465      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
23466      * anchor positions (defaults to 'tl-bl')
23467      */
23468     listAlign: 'tl-bl?',
23469     /**
23470      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
23471      */
23472     maxHeight: 300,
23473     /**
23474      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
23475      * query specified by the allQuery config option (defaults to 'query')
23476      */
23477     triggerAction: 'query',
23478     /**
23479      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
23480      * (defaults to 4, does not apply if editable = false)
23481      */
23482     minChars : 4,
23483     /**
23484      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
23485      * delay (typeAheadDelay) if it matches a known value (defaults to false)
23486      */
23487     typeAhead: false,
23488     /**
23489      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
23490      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
23491      */
23492     queryDelay: 500,
23493     /**
23494      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
23495      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
23496      */
23497     pageSize: 0,
23498     /**
23499      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
23500      * when editable = true (defaults to false)
23501      */
23502     selectOnFocus:false,
23503     /**
23504      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
23505      */
23506     queryParam: 'query',
23507     /**
23508      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
23509      * when mode = 'remote' (defaults to 'Loading...')
23510      */
23511     loadingText: 'Loading...',
23512     /**
23513      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
23514      */
23515     resizable: false,
23516     /**
23517      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
23518      */
23519     handleHeight : 8,
23520     /**
23521      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
23522      * traditional select (defaults to true)
23523      */
23524     editable: true,
23525     /**
23526      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
23527      */
23528     allQuery: '',
23529     /**
23530      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
23531      */
23532     mode: 'remote',
23533     /**
23534      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
23535      * listWidth has a higher value)
23536      */
23537     minListWidth : 70,
23538     /**
23539      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
23540      * allow the user to set arbitrary text into the field (defaults to false)
23541      */
23542     forceSelection:false,
23543     /**
23544      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
23545      * if typeAhead = true (defaults to 250)
23546      */
23547     typeAheadDelay : 250,
23548     /**
23549      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
23550      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
23551      */
23552     valueNotFoundText : undefined,
23553     /**
23554      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
23555      */
23556     blockFocus : false,
23557     
23558     /**
23559      * @cfg {Boolean} disableClear Disable showing of clear button.
23560      */
23561     disableClear : false,
23562     /**
23563      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
23564      */
23565     alwaysQuery : false,
23566     
23567     //private
23568     addicon : false,
23569     editicon: false,
23570     
23571     // element that contains real text value.. (when hidden is used..)
23572      
23573     // private
23574     onRender : function(ct, position){
23575         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
23576         if(this.hiddenName){
23577             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
23578                     'before', true);
23579             this.hiddenField.value =
23580                 this.hiddenValue !== undefined ? this.hiddenValue :
23581                 this.value !== undefined ? this.value : '';
23582
23583             // prevent input submission
23584             this.el.dom.removeAttribute('name');
23585              
23586              
23587         }
23588         if(Roo.isGecko){
23589             this.el.dom.setAttribute('autocomplete', 'off');
23590         }
23591
23592         var cls = 'x-combo-list';
23593
23594         this.list = new Roo.Layer({
23595             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
23596         });
23597
23598         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
23599         this.list.setWidth(lw);
23600         this.list.swallowEvent('mousewheel');
23601         this.assetHeight = 0;
23602
23603         if(this.title){
23604             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
23605             this.assetHeight += this.header.getHeight();
23606         }
23607
23608         this.innerList = this.list.createChild({cls:cls+'-inner'});
23609         this.innerList.on('mouseover', this.onViewOver, this);
23610         this.innerList.on('mousemove', this.onViewMove, this);
23611         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23612         
23613         if(this.allowBlank && !this.pageSize && !this.disableClear){
23614             this.footer = this.list.createChild({cls:cls+'-ft'});
23615             this.pageTb = new Roo.Toolbar(this.footer);
23616            
23617         }
23618         if(this.pageSize){
23619             this.footer = this.list.createChild({cls:cls+'-ft'});
23620             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
23621                     {pageSize: this.pageSize});
23622             
23623         }
23624         
23625         if (this.pageTb && this.allowBlank && !this.disableClear) {
23626             var _this = this;
23627             this.pageTb.add(new Roo.Toolbar.Fill(), {
23628                 cls: 'x-btn-icon x-btn-clear',
23629                 text: '&#160;',
23630                 handler: function()
23631                 {
23632                     _this.collapse();
23633                     _this.clearValue();
23634                     _this.onSelect(false, -1);
23635                 }
23636             });
23637         }
23638         if (this.footer) {
23639             this.assetHeight += this.footer.getHeight();
23640         }
23641         
23642
23643         if(!this.tpl){
23644             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
23645         }
23646
23647         this.view = new Roo.View(this.innerList, this.tpl, {
23648             singleSelect:true, store: this.store, selectedClass: this.selectedClass
23649         });
23650
23651         this.view.on('click', this.onViewClick, this);
23652
23653         this.store.on('beforeload', this.onBeforeLoad, this);
23654         this.store.on('load', this.onLoad, this);
23655         this.store.on('loadexception', this.onLoadException, this);
23656
23657         if(this.resizable){
23658             this.resizer = new Roo.Resizable(this.list,  {
23659                pinned:true, handles:'se'
23660             });
23661             this.resizer.on('resize', function(r, w, h){
23662                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
23663                 this.listWidth = w;
23664                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
23665                 this.restrictHeight();
23666             }, this);
23667             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
23668         }
23669         if(!this.editable){
23670             this.editable = true;
23671             this.setEditable(false);
23672         }  
23673         
23674         
23675         if (typeof(this.events.add.listeners) != 'undefined') {
23676             
23677             this.addicon = this.wrap.createChild(
23678                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
23679        
23680             this.addicon.on('click', function(e) {
23681                 this.fireEvent('add', this);
23682             }, this);
23683         }
23684         if (typeof(this.events.edit.listeners) != 'undefined') {
23685             
23686             this.editicon = this.wrap.createChild(
23687                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
23688             if (this.addicon) {
23689                 this.editicon.setStyle('margin-left', '40px');
23690             }
23691             this.editicon.on('click', function(e) {
23692                 
23693                 // we fire even  if inothing is selected..
23694                 this.fireEvent('edit', this, this.lastData );
23695                 
23696             }, this);
23697         }
23698         
23699         
23700         
23701     },
23702
23703     // private
23704     initEvents : function(){
23705         Roo.form.ComboBox.superclass.initEvents.call(this);
23706
23707         this.keyNav = new Roo.KeyNav(this.el, {
23708             "up" : function(e){
23709                 this.inKeyMode = true;
23710                 this.selectPrev();
23711             },
23712
23713             "down" : function(e){
23714                 if(!this.isExpanded()){
23715                     this.onTriggerClick();
23716                 }else{
23717                     this.inKeyMode = true;
23718                     this.selectNext();
23719                 }
23720             },
23721
23722             "enter" : function(e){
23723                 this.onViewClick();
23724                 //return true;
23725             },
23726
23727             "esc" : function(e){
23728                 this.collapse();
23729             },
23730
23731             "tab" : function(e){
23732                 this.onViewClick(false);
23733                 this.fireEvent("specialkey", this, e);
23734                 return true;
23735             },
23736
23737             scope : this,
23738
23739             doRelay : function(foo, bar, hname){
23740                 if(hname == 'down' || this.scope.isExpanded()){
23741                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
23742                 }
23743                 return true;
23744             },
23745
23746             forceKeyDown: true
23747         });
23748         this.queryDelay = Math.max(this.queryDelay || 10,
23749                 this.mode == 'local' ? 10 : 250);
23750         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
23751         if(this.typeAhead){
23752             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
23753         }
23754         if(this.editable !== false){
23755             this.el.on("keyup", this.onKeyUp, this);
23756         }
23757         if(this.forceSelection){
23758             this.on('blur', this.doForce, this);
23759         }
23760     },
23761
23762     onDestroy : function(){
23763         if(this.view){
23764             this.view.setStore(null);
23765             this.view.el.removeAllListeners();
23766             this.view.el.remove();
23767             this.view.purgeListeners();
23768         }
23769         if(this.list){
23770             this.list.destroy();
23771         }
23772         if(this.store){
23773             this.store.un('beforeload', this.onBeforeLoad, this);
23774             this.store.un('load', this.onLoad, this);
23775             this.store.un('loadexception', this.onLoadException, this);
23776         }
23777         Roo.form.ComboBox.superclass.onDestroy.call(this);
23778     },
23779
23780     // private
23781     fireKey : function(e){
23782         if(e.isNavKeyPress() && !this.list.isVisible()){
23783             this.fireEvent("specialkey", this, e);
23784         }
23785     },
23786
23787     // private
23788     onResize: function(w, h){
23789         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
23790         
23791         if(typeof w != 'number'){
23792             // we do not handle it!?!?
23793             return;
23794         }
23795         var tw = this.trigger.getWidth();
23796         tw += this.addicon ? this.addicon.getWidth() : 0;
23797         tw += this.editicon ? this.editicon.getWidth() : 0;
23798         var x = w - tw;
23799         this.el.setWidth( this.adjustWidth('input', x));
23800             
23801         this.trigger.setStyle('left', x+'px');
23802         
23803         if(this.list && this.listWidth === undefined){
23804             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
23805             this.list.setWidth(lw);
23806             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23807         }
23808         
23809     
23810         
23811     },
23812
23813     /**
23814      * Allow or prevent the user from directly editing the field text.  If false is passed,
23815      * the user will only be able to select from the items defined in the dropdown list.  This method
23816      * is the runtime equivalent of setting the 'editable' config option at config time.
23817      * @param {Boolean} value True to allow the user to directly edit the field text
23818      */
23819     setEditable : function(value){
23820         if(value == this.editable){
23821             return;
23822         }
23823         this.editable = value;
23824         if(!value){
23825             this.el.dom.setAttribute('readOnly', true);
23826             this.el.on('mousedown', this.onTriggerClick,  this);
23827             this.el.addClass('x-combo-noedit');
23828         }else{
23829             this.el.dom.setAttribute('readOnly', false);
23830             this.el.un('mousedown', this.onTriggerClick,  this);
23831             this.el.removeClass('x-combo-noedit');
23832         }
23833     },
23834
23835     // private
23836     onBeforeLoad : function(){
23837         if(!this.hasFocus){
23838             return;
23839         }
23840         this.innerList.update(this.loadingText ?
23841                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
23842         this.restrictHeight();
23843         this.selectedIndex = -1;
23844     },
23845
23846     // private
23847     onLoad : function(){
23848         if(!this.hasFocus){
23849             return;
23850         }
23851         if(this.store.getCount() > 0){
23852             this.expand();
23853             this.restrictHeight();
23854             if(this.lastQuery == this.allQuery){
23855                 if(this.editable){
23856                     this.el.dom.select();
23857                 }
23858                 if(!this.selectByValue(this.value, true)){
23859                     this.select(0, true);
23860                 }
23861             }else{
23862                 this.selectNext();
23863                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
23864                     this.taTask.delay(this.typeAheadDelay);
23865                 }
23866             }
23867         }else{
23868             this.onEmptyResults();
23869         }
23870         //this.el.focus();
23871     },
23872     // private
23873     onLoadException : function()
23874     {
23875         this.collapse();
23876         Roo.log(this.store.reader.jsonData);
23877         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
23878             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
23879         }
23880         
23881         
23882     },
23883     // private
23884     onTypeAhead : function(){
23885         if(this.store.getCount() > 0){
23886             var r = this.store.getAt(0);
23887             var newValue = r.data[this.displayField];
23888             var len = newValue.length;
23889             var selStart = this.getRawValue().length;
23890             if(selStart != len){
23891                 this.setRawValue(newValue);
23892                 this.selectText(selStart, newValue.length);
23893             }
23894         }
23895     },
23896
23897     // private
23898     onSelect : function(record, index){
23899         if(this.fireEvent('beforeselect', this, record, index) !== false){
23900             this.setFromData(index > -1 ? record.data : false);
23901             this.collapse();
23902             this.fireEvent('select', this, record, index);
23903         }
23904     },
23905
23906     /**
23907      * Returns the currently selected field value or empty string if no value is set.
23908      * @return {String} value The selected value
23909      */
23910     getValue : function(){
23911         if(this.valueField){
23912             return typeof this.value != 'undefined' ? this.value : '';
23913         }else{
23914             return Roo.form.ComboBox.superclass.getValue.call(this);
23915         }
23916     },
23917
23918     /**
23919      * Clears any text/value currently set in the field
23920      */
23921     clearValue : function(){
23922         if(this.hiddenField){
23923             this.hiddenField.value = '';
23924         }
23925         this.value = '';
23926         this.setRawValue('');
23927         this.lastSelectionText = '';
23928         this.applyEmptyText();
23929     },
23930
23931     /**
23932      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
23933      * will be displayed in the field.  If the value does not match the data value of an existing item,
23934      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
23935      * Otherwise the field will be blank (although the value will still be set).
23936      * @param {String} value The value to match
23937      */
23938     setValue : function(v){
23939         var text = v;
23940         if(this.valueField){
23941             var r = this.findRecord(this.valueField, v);
23942             if(r){
23943                 text = r.data[this.displayField];
23944             }else if(this.valueNotFoundText !== undefined){
23945                 text = this.valueNotFoundText;
23946             }
23947         }
23948         this.lastSelectionText = text;
23949         if(this.hiddenField){
23950             this.hiddenField.value = v;
23951         }
23952         Roo.form.ComboBox.superclass.setValue.call(this, text);
23953         this.value = v;
23954     },
23955     /**
23956      * @property {Object} the last set data for the element
23957      */
23958     
23959     lastData : false,
23960     /**
23961      * Sets the value of the field based on a object which is related to the record format for the store.
23962      * @param {Object} value the value to set as. or false on reset?
23963      */
23964     setFromData : function(o){
23965         var dv = ''; // display value
23966         var vv = ''; // value value..
23967         this.lastData = o;
23968         if (this.displayField) {
23969             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
23970         } else {
23971             // this is an error condition!!!
23972             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
23973         }
23974         
23975         if(this.valueField){
23976             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
23977         }
23978         if(this.hiddenField){
23979             this.hiddenField.value = vv;
23980             
23981             this.lastSelectionText = dv;
23982             Roo.form.ComboBox.superclass.setValue.call(this, dv);
23983             this.value = vv;
23984             return;
23985         }
23986         // no hidden field.. - we store the value in 'value', but still display
23987         // display field!!!!
23988         this.lastSelectionText = dv;
23989         Roo.form.ComboBox.superclass.setValue.call(this, dv);
23990         this.value = vv;
23991         
23992         
23993     },
23994     // private
23995     reset : function(){
23996         // overridden so that last data is reset..
23997         this.setValue(this.originalValue);
23998         this.clearInvalid();
23999         this.lastData = false;
24000     },
24001     // private
24002     findRecord : function(prop, value){
24003         var record;
24004         if(this.store.getCount() > 0){
24005             this.store.each(function(r){
24006                 if(r.data[prop] == value){
24007                     record = r;
24008                     return false;
24009                 }
24010                 return true;
24011             });
24012         }
24013         return record;
24014     },
24015     
24016     getName: function()
24017     {
24018         // returns hidden if it's set..
24019         if (!this.rendered) {return ''};
24020         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
24021         
24022     },
24023     // private
24024     onViewMove : function(e, t){
24025         this.inKeyMode = false;
24026     },
24027
24028     // private
24029     onViewOver : function(e, t){
24030         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
24031             return;
24032         }
24033         var item = this.view.findItemFromChild(t);
24034         if(item){
24035             var index = this.view.indexOf(item);
24036             this.select(index, false);
24037         }
24038     },
24039
24040     // private
24041     onViewClick : function(doFocus)
24042     {
24043         var index = this.view.getSelectedIndexes()[0];
24044         var r = this.store.getAt(index);
24045         if(r){
24046             this.onSelect(r, index);
24047         }
24048         if(doFocus !== false && !this.blockFocus){
24049             this.el.focus();
24050         }
24051     },
24052
24053     // private
24054     restrictHeight : function(){
24055         this.innerList.dom.style.height = '';
24056         var inner = this.innerList.dom;
24057         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
24058         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
24059         this.list.beginUpdate();
24060         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
24061         this.list.alignTo(this.el, this.listAlign);
24062         this.list.endUpdate();
24063     },
24064
24065     // private
24066     onEmptyResults : function(){
24067         this.collapse();
24068     },
24069
24070     /**
24071      * Returns true if the dropdown list is expanded, else false.
24072      */
24073     isExpanded : function(){
24074         return this.list.isVisible();
24075     },
24076
24077     /**
24078      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
24079      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
24080      * @param {String} value The data value of the item to select
24081      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
24082      * selected item if it is not currently in view (defaults to true)
24083      * @return {Boolean} True if the value matched an item in the list, else false
24084      */
24085     selectByValue : function(v, scrollIntoView){
24086         if(v !== undefined && v !== null){
24087             var r = this.findRecord(this.valueField || this.displayField, v);
24088             if(r){
24089                 this.select(this.store.indexOf(r), scrollIntoView);
24090                 return true;
24091             }
24092         }
24093         return false;
24094     },
24095
24096     /**
24097      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
24098      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
24099      * @param {Number} index The zero-based index of the list item to select
24100      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
24101      * selected item if it is not currently in view (defaults to true)
24102      */
24103     select : function(index, scrollIntoView){
24104         this.selectedIndex = index;
24105         this.view.select(index);
24106         if(scrollIntoView !== false){
24107             var el = this.view.getNode(index);
24108             if(el){
24109                 this.innerList.scrollChildIntoView(el, false);
24110             }
24111         }
24112     },
24113
24114     // private
24115     selectNext : function(){
24116         var ct = this.store.getCount();
24117         if(ct > 0){
24118             if(this.selectedIndex == -1){
24119                 this.select(0);
24120             }else if(this.selectedIndex < ct-1){
24121                 this.select(this.selectedIndex+1);
24122             }
24123         }
24124     },
24125
24126     // private
24127     selectPrev : function(){
24128         var ct = this.store.getCount();
24129         if(ct > 0){
24130             if(this.selectedIndex == -1){
24131                 this.select(0);
24132             }else if(this.selectedIndex != 0){
24133                 this.select(this.selectedIndex-1);
24134             }
24135         }
24136     },
24137
24138     // private
24139     onKeyUp : function(e){
24140         if(this.editable !== false && !e.isSpecialKey()){
24141             this.lastKey = e.getKey();
24142             this.dqTask.delay(this.queryDelay);
24143         }
24144     },
24145
24146     // private
24147     validateBlur : function(){
24148         return !this.list || !this.list.isVisible();   
24149     },
24150
24151     // private
24152     initQuery : function(){
24153         this.doQuery(this.getRawValue());
24154     },
24155
24156     // private
24157     doForce : function(){
24158         if(this.el.dom.value.length > 0){
24159             this.el.dom.value =
24160                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
24161             this.applyEmptyText();
24162         }
24163     },
24164
24165     /**
24166      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
24167      * query allowing the query action to be canceled if needed.
24168      * @param {String} query The SQL query to execute
24169      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
24170      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
24171      * saved in the current store (defaults to false)
24172      */
24173     doQuery : function(q, forceAll){
24174         if(q === undefined || q === null){
24175             q = '';
24176         }
24177         var qe = {
24178             query: q,
24179             forceAll: forceAll,
24180             combo: this,
24181             cancel:false
24182         };
24183         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
24184             return false;
24185         }
24186         q = qe.query;
24187         forceAll = qe.forceAll;
24188         if(forceAll === true || (q.length >= this.minChars)){
24189             if(this.lastQuery != q || this.alwaysQuery){
24190                 this.lastQuery = q;
24191                 if(this.mode == 'local'){
24192                     this.selectedIndex = -1;
24193                     if(forceAll){
24194                         this.store.clearFilter();
24195                     }else{
24196                         this.store.filter(this.displayField, q);
24197                     }
24198                     this.onLoad();
24199                 }else{
24200                     this.store.baseParams[this.queryParam] = q;
24201                     this.store.load({
24202                         params: this.getParams(q)
24203                     });
24204                     this.expand();
24205                 }
24206             }else{
24207                 this.selectedIndex = -1;
24208                 this.onLoad();   
24209             }
24210         }
24211     },
24212
24213     // private
24214     getParams : function(q){
24215         var p = {};
24216         //p[this.queryParam] = q;
24217         if(this.pageSize){
24218             p.start = 0;
24219             p.limit = this.pageSize;
24220         }
24221         return p;
24222     },
24223
24224     /**
24225      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
24226      */
24227     collapse : function(){
24228         if(!this.isExpanded()){
24229             return;
24230         }
24231         this.list.hide();
24232         Roo.get(document).un('mousedown', this.collapseIf, this);
24233         Roo.get(document).un('mousewheel', this.collapseIf, this);
24234         if (!this.editable) {
24235             Roo.get(document).un('keydown', this.listKeyPress, this);
24236         }
24237         this.fireEvent('collapse', this);
24238     },
24239
24240     // private
24241     collapseIf : function(e){
24242         if(!e.within(this.wrap) && !e.within(this.list)){
24243             this.collapse();
24244         }
24245     },
24246
24247     /**
24248      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
24249      */
24250     expand : function(){
24251         if(this.isExpanded() || !this.hasFocus){
24252             return;
24253         }
24254         this.list.alignTo(this.el, this.listAlign);
24255         this.list.show();
24256         Roo.get(document).on('mousedown', this.collapseIf, this);
24257         Roo.get(document).on('mousewheel', this.collapseIf, this);
24258         if (!this.editable) {
24259             Roo.get(document).on('keydown', this.listKeyPress, this);
24260         }
24261         
24262         this.fireEvent('expand', this);
24263     },
24264
24265     // private
24266     // Implements the default empty TriggerField.onTriggerClick function
24267     onTriggerClick : function(){
24268         if(this.disabled){
24269             return;
24270         }
24271         if(this.isExpanded()){
24272             this.collapse();
24273             if (!this.blockFocus) {
24274                 this.el.focus();
24275             }
24276             
24277         }else {
24278             this.hasFocus = true;
24279             if(this.triggerAction == 'all') {
24280                 this.doQuery(this.allQuery, true);
24281             } else {
24282                 this.doQuery(this.getRawValue());
24283             }
24284             if (!this.blockFocus) {
24285                 this.el.focus();
24286             }
24287         }
24288     },
24289     listKeyPress : function(e)
24290     {
24291         //Roo.log('listkeypress');
24292         // scroll to first matching element based on key pres..
24293         if (e.isSpecialKey()) {
24294             return false;
24295         }
24296         var k = String.fromCharCode(e.getKey()).toUpperCase();
24297         //Roo.log(k);
24298         var match  = false;
24299         var csel = this.view.getSelectedNodes();
24300         var cselitem = false;
24301         if (csel.length) {
24302             var ix = this.view.indexOf(csel[0]);
24303             cselitem  = this.store.getAt(ix);
24304             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
24305                 cselitem = false;
24306             }
24307             
24308         }
24309         
24310         this.store.each(function(v) { 
24311             if (cselitem) {
24312                 // start at existing selection.
24313                 if (cselitem.id == v.id) {
24314                     cselitem = false;
24315                 }
24316                 return;
24317             }
24318                 
24319             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
24320                 match = this.store.indexOf(v);
24321                 return false;
24322             }
24323         }, this);
24324         
24325         if (match === false) {
24326             return true; // no more action?
24327         }
24328         // scroll to?
24329         this.view.select(match);
24330         var sn = Roo.get(this.view.getSelectedNodes()[0])
24331         sn.scrollIntoView(sn.dom.parentNode, false);
24332     }
24333
24334     /** 
24335     * @cfg {Boolean} grow 
24336     * @hide 
24337     */
24338     /** 
24339     * @cfg {Number} growMin 
24340     * @hide 
24341     */
24342     /** 
24343     * @cfg {Number} growMax 
24344     * @hide 
24345     */
24346     /**
24347      * @hide
24348      * @method autoSize
24349      */
24350 });/*
24351  * Copyright(c) 2010-2012, Roo J Solutions Limited
24352  *
24353  * Licence LGPL
24354  *
24355  */
24356
24357 /**
24358  * @class Roo.form.ComboBoxArray
24359  * @extends Roo.form.TextField
24360  * A facebook style adder... for lists of email / people / countries  etc...
24361  * pick multiple items from a combo box, and shows each one.
24362  *
24363  *  Fred [x]  Brian [x]  [Pick another |v]
24364  *
24365  *
24366  *  For this to work: it needs various extra information
24367  *    - normal combo problay has
24368  *      name, hiddenName
24369  *    + displayField, valueField
24370  *
24371  *    For our purpose...
24372  *
24373  *
24374  *   If we change from 'extends' to wrapping...
24375  *   
24376  *  
24377  *
24378  
24379  
24380  * @constructor
24381  * Create a new ComboBoxArray.
24382  * @param {Object} config Configuration options
24383  */
24384  
24385
24386 Roo.form.ComboBoxArray = function(config)
24387 {
24388     
24389     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
24390     
24391     this.items = new Roo.util.MixedCollection(false);
24392     
24393     // construct the child combo...
24394     
24395     
24396     
24397     
24398    
24399     
24400 }
24401
24402  
24403 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
24404
24405     /**
24406      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
24407      */
24408     
24409     lastData : false,
24410     
24411     // behavies liek a hiddne field
24412     inputType:      'hidden',
24413     /**
24414      * @cfg {Number} width The width of the box that displays the selected element
24415      */ 
24416     width:          300,
24417
24418     
24419     
24420     /**
24421      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
24422      */
24423     name : false,
24424     /**
24425      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
24426      */
24427     hiddenName : false,
24428     
24429     
24430     // private the array of items that are displayed..
24431     items  : false,
24432     // private - the hidden field el.
24433     hiddenEl : false,
24434     // private - the filed el..
24435     el : false,
24436     
24437     //validateValue : function() { return true; }, // all values are ok!
24438     //onAddClick: function() { },
24439     
24440     onRender : function(ct, position) 
24441     {
24442         
24443         // create the standard hidden element
24444         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
24445         
24446         
24447         // give fake names to child combo;
24448         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
24449         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
24450         
24451         this.combo = Roo.factory(this.combo, Roo.form);
24452         this.combo.onRender(ct, position);
24453         
24454         // assigned so form know we need to do this..
24455         this.store          = this.combo.store;
24456         this.valueField     = this.combo.valueField;
24457         this.displayField   = this.combo.displayField ;
24458         
24459         
24460         this.combo.wrap.addClass('x-cbarray-grp');
24461         
24462         var cbwrap = this.combo.wrap.createChild(
24463             {tag: 'div', cls: 'x-cbarray-cb'},
24464             this.combo.el.dom
24465         );
24466         
24467              
24468         this.hiddenEl = this.combo.wrap.createChild({
24469             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
24470         });
24471         this.el = this.combo.wrap.createChild({
24472             tag: 'input',  type:'hidden' , name: this.name, value : ''
24473         });
24474          //   this.el.dom.removeAttribute("name");
24475         
24476         
24477         this.outerWrap = this.combo.wrap;
24478         this.wrap = cbwrap;
24479         
24480         this.outerWrap.setWidth(this.width);
24481         this.outerWrap.dom.removeChild(this.el.dom);
24482         
24483         this.wrap.dom.appendChild(this.el.dom);
24484         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
24485         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
24486         
24487         this.combo.trigger.setStyle('position','relative');
24488         this.combo.trigger.setStyle('left', '0px');
24489         this.combo.trigger.setStyle('top', '2px');
24490         
24491         this.combo.el.setStyle('vertical-align', 'text-bottom');
24492         
24493         //this.trigger.setStyle('vertical-align', 'top');
24494         
24495         // this should use the code from combo really... on('add' ....)
24496         if (this.adder) {
24497             
24498         
24499             this.adder = this.outerWrap.createChild(
24500                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
24501             var _t = this;
24502             this.adder.on('click', function(e) {
24503                 _t.fireEvent('adderclick', this, e);
24504             }, _t);
24505         }
24506         //var _t = this;
24507         //this.adder.on('click', this.onAddClick, _t);
24508         
24509         
24510         this.combo.on('select', function(cb, rec, ix) {
24511             this.addItem(rec.data);
24512             
24513             cb.setValue('');
24514             cb.el.dom.value = '';
24515             //cb.lastData = rec.data;
24516             // add to list
24517             
24518         }, this);
24519         
24520         
24521     },
24522     
24523     
24524     getName: function()
24525     {
24526         // returns hidden if it's set..
24527         if (!this.rendered) {return ''};
24528         return  this.hiddenName ? this.hiddenName : this.name;
24529         
24530     },
24531     
24532     
24533     onResize: function(w, h){
24534         
24535         return;
24536         // not sure if this is needed..
24537         //this.combo.onResize(w,h);
24538         
24539         if(typeof w != 'number'){
24540             // we do not handle it!?!?
24541             return;
24542         }
24543         var tw = this.combo.trigger.getWidth();
24544         tw += this.addicon ? this.addicon.getWidth() : 0;
24545         tw += this.editicon ? this.editicon.getWidth() : 0;
24546         var x = w - tw;
24547         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
24548             
24549         this.combo.trigger.setStyle('left', '0px');
24550         
24551         if(this.list && this.listWidth === undefined){
24552             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
24553             this.list.setWidth(lw);
24554             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
24555         }
24556         
24557     
24558         
24559     },
24560     
24561     addItem: function(rec)
24562     {
24563         var valueField = this.combo.valueField;
24564         var displayField = this.combo.displayField;
24565         if (this.items.indexOfKey(rec[valueField]) > -1) {
24566             //console.log("GOT " + rec.data.id);
24567             return;
24568         }
24569         
24570         var x = new Roo.form.ComboBoxArray.Item({
24571             //id : rec[this.idField],
24572             data : rec,
24573             displayField : displayField ,
24574             tipField : displayField ,
24575             cb : this
24576         });
24577         // use the 
24578         this.items.add(rec[valueField],x);
24579         // add it before the element..
24580         this.updateHiddenEl();
24581         x.render(this.outerWrap, this.wrap.dom);
24582         // add the image handler..
24583     },
24584     
24585     updateHiddenEl : function()
24586     {
24587         this.validate();
24588         if (!this.hiddenEl) {
24589             return;
24590         }
24591         var ar = [];
24592         var idField = this.combo.valueField;
24593         
24594         this.items.each(function(f) {
24595             ar.push(f.data[idField]);
24596            
24597         });
24598         this.hiddenEl.dom.value = ar.join(',');
24599         this.validate();
24600     },
24601     
24602     reset : function()
24603     {
24604         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
24605         this.items.each(function(f) {
24606            f.remove(); 
24607         });
24608         this.el.dom.value = '';
24609         if (this.hiddenEl) {
24610             this.hiddenEl.dom.value = '';
24611         }
24612         
24613     },
24614     getValue: function()
24615     {
24616         return this.hiddenEl ? this.hiddenEl.dom.value : '';
24617     },
24618     setValue: function(v) // not a valid action - must use addItems..
24619     {
24620          
24621         this.reset();
24622         
24623         
24624         
24625         if (this.store.isLocal && (typeof(v) == 'string')) {
24626             // then we can use the store to find the values..
24627             // comma seperated at present.. this needs to allow JSON based encoding..
24628             this.hiddenEl.value  = v;
24629             var v_ar = [];
24630             Roo.each(v.split(','), function(k) {
24631                 Roo.log("CHECK " + this.valueField + ',' + k);
24632                 var li = this.store.query(this.valueField, k);
24633                 if (!li.length) {
24634                     return;
24635                 }
24636                 add = {};
24637                 add[this.valueField] = k;
24638                 add[this.displayField] = li.item(0).data[this.displayField];
24639                 
24640                 this.addItem(add);
24641             }, this) 
24642              
24643         }
24644         if (typeof(v) == 'object') {
24645             // then let's assume it's an array of objects..
24646             Roo.each(v, function(l) {
24647                 this.addItem(l);
24648             }, this);
24649              
24650         }
24651         
24652         
24653     },
24654     setFromData: function(v)
24655     {
24656         // this recieves an object, if setValues is called.
24657         this.reset();
24658         this.el.dom.value = v[this.displayField];
24659         this.hiddenEl.dom.value = v[this.valueField];
24660         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
24661             return;
24662         }
24663         var kv = v[this.valueField];
24664         var dv = v[this.displayField];
24665         kv = typeof(kv) != 'string' ? '' : kv;
24666         dv = typeof(dv) != 'string' ? '' : dv;
24667         
24668         
24669         var keys = kv.split(',');
24670         var display = dv.split(',');
24671         for (var i = 0 ; i < keys.length; i++) {
24672             
24673             add = {};
24674             add[this.valueField] = keys[i];
24675             add[this.displayField] = display[i];
24676             this.addItem(add);
24677         }
24678       
24679         
24680     },
24681     
24682     
24683     validateValue : function(value){
24684         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
24685         
24686     }
24687     
24688 });
24689
24690
24691
24692 /**
24693  * @class Roo.form.ComboBoxArray.Item
24694  * @extends Roo.BoxComponent
24695  * A selected item in the list
24696  *  Fred [x]  Brian [x]  [Pick another |v]
24697  * 
24698  * @constructor
24699  * Create a new item.
24700  * @param {Object} config Configuration options
24701  */
24702  
24703 Roo.form.ComboBoxArray.Item = function(config) {
24704     config.id = Roo.id();
24705     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
24706 }
24707
24708 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
24709     data : {},
24710     cb: false,
24711     displayField : false,
24712     tipField : false,
24713     
24714     
24715     defaultAutoCreate : {
24716         tag: 'div',
24717         cls: 'x-cbarray-item',
24718         cn : [ 
24719             { tag: 'div' },
24720             {
24721                 tag: 'img',
24722                 width:16,
24723                 height : 16,
24724                 src : Roo.BLANK_IMAGE_URL ,
24725                 align: 'center'
24726             }
24727         ]
24728         
24729     },
24730     
24731  
24732     onRender : function(ct, position)
24733     {
24734         Roo.form.Field.superclass.onRender.call(this, ct, position);
24735         
24736         if(!this.el){
24737             var cfg = this.getAutoCreate();
24738             this.el = ct.createChild(cfg, position);
24739         }
24740         
24741         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
24742         
24743         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
24744             this.cb.renderer(this.data) :
24745             String.format('{0}',this.data[this.displayField]);
24746         
24747             
24748         this.el.child('div').dom.setAttribute('qtip',
24749                         String.format('{0}',this.data[this.tipField])
24750         );
24751         
24752         this.el.child('img').on('click', this.remove, this);
24753         
24754     },
24755    
24756     remove : function()
24757     {
24758         
24759         this.cb.items.remove(this);
24760         this.el.child('img').un('click', this.remove, this);
24761         this.el.remove();
24762         this.cb.updateHiddenEl();
24763     }
24764     
24765     
24766 });/*
24767  * Based on:
24768  * Ext JS Library 1.1.1
24769  * Copyright(c) 2006-2007, Ext JS, LLC.
24770  *
24771  * Originally Released Under LGPL - original licence link has changed is not relivant.
24772  *
24773  * Fork - LGPL
24774  * <script type="text/javascript">
24775  */
24776 /**
24777  * @class Roo.form.Checkbox
24778  * @extends Roo.form.Field
24779  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
24780  * @constructor
24781  * Creates a new Checkbox
24782  * @param {Object} config Configuration options
24783  */
24784 Roo.form.Checkbox = function(config){
24785     Roo.form.Checkbox.superclass.constructor.call(this, config);
24786     this.addEvents({
24787         /**
24788          * @event check
24789          * Fires when the checkbox is checked or unchecked.
24790              * @param {Roo.form.Checkbox} this This checkbox
24791              * @param {Boolean} checked The new checked value
24792              */
24793         check : true
24794     });
24795 };
24796
24797 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
24798     /**
24799      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
24800      */
24801     focusClass : undefined,
24802     /**
24803      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
24804      */
24805     fieldClass: "x-form-field",
24806     /**
24807      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
24808      */
24809     checked: false,
24810     /**
24811      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
24812      * {tag: "input", type: "checkbox", autocomplete: "off"})
24813      */
24814     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
24815     /**
24816      * @cfg {String} boxLabel The text that appears beside the checkbox
24817      */
24818     boxLabel : "",
24819     /**
24820      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
24821      */  
24822     inputValue : '1',
24823     /**
24824      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24825      */
24826      valueOff: '0', // value when not checked..
24827
24828     actionMode : 'viewEl', 
24829     //
24830     // private
24831     itemCls : 'x-menu-check-item x-form-item',
24832     groupClass : 'x-menu-group-item',
24833     inputType : 'hidden',
24834     
24835     
24836     inSetChecked: false, // check that we are not calling self...
24837     
24838     inputElement: false, // real input element?
24839     basedOn: false, // ????
24840     
24841     isFormField: true, // not sure where this is needed!!!!
24842
24843     onResize : function(){
24844         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
24845         if(!this.boxLabel){
24846             this.el.alignTo(this.wrap, 'c-c');
24847         }
24848     },
24849
24850     initEvents : function(){
24851         Roo.form.Checkbox.superclass.initEvents.call(this);
24852         this.el.on("click", this.onClick,  this);
24853         this.el.on("change", this.onClick,  this);
24854     },
24855
24856
24857     getResizeEl : function(){
24858         return this.wrap;
24859     },
24860
24861     getPositionEl : function(){
24862         return this.wrap;
24863     },
24864
24865     // private
24866     onRender : function(ct, position){
24867         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
24868         /*
24869         if(this.inputValue !== undefined){
24870             this.el.dom.value = this.inputValue;
24871         }
24872         */
24873         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
24874         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
24875         var viewEl = this.wrap.createChild({ 
24876             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
24877         this.viewEl = viewEl;   
24878         this.wrap.on('click', this.onClick,  this); 
24879         
24880         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
24881         this.el.on('propertychange', this.setFromHidden,  this);  //ie
24882         
24883         
24884         
24885         if(this.boxLabel){
24886             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
24887         //    viewEl.on('click', this.onClick,  this); 
24888         }
24889         //if(this.checked){
24890             this.setChecked(this.checked);
24891         //}else{
24892             //this.checked = this.el.dom;
24893         //}
24894
24895     },
24896
24897     // private
24898     initValue : Roo.emptyFn,
24899
24900     /**
24901      * Returns the checked state of the checkbox.
24902      * @return {Boolean} True if checked, else false
24903      */
24904     getValue : function(){
24905         if(this.el){
24906             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
24907         }
24908         return this.valueOff;
24909         
24910     },
24911
24912         // private
24913     onClick : function(){ 
24914         this.setChecked(!this.checked);
24915
24916         //if(this.el.dom.checked != this.checked){
24917         //    this.setValue(this.el.dom.checked);
24918        // }
24919     },
24920
24921     /**
24922      * Sets the checked state of the checkbox.
24923      * On is always based on a string comparison between inputValue and the param.
24924      * @param {Boolean/String} value - the value to set 
24925      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
24926      */
24927     setValue : function(v,suppressEvent){
24928         
24929         
24930         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
24931         //if(this.el && this.el.dom){
24932         //    this.el.dom.checked = this.checked;
24933         //    this.el.dom.defaultChecked = this.checked;
24934         //}
24935         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
24936         //this.fireEvent("check", this, this.checked);
24937     },
24938     // private..
24939     setChecked : function(state,suppressEvent)
24940     {
24941         if (this.inSetChecked) {
24942             this.checked = state;
24943             return;
24944         }
24945         
24946     
24947         if(this.wrap){
24948             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
24949         }
24950         this.checked = state;
24951         if(suppressEvent !== true){
24952             this.fireEvent('check', this, state);
24953         }
24954         this.inSetChecked = true;
24955         this.el.dom.value = state ? this.inputValue : this.valueOff;
24956         this.inSetChecked = false;
24957         
24958     },
24959     // handle setting of hidden value by some other method!!?!?
24960     setFromHidden: function()
24961     {
24962         if(!this.el){
24963             return;
24964         }
24965         //console.log("SET FROM HIDDEN");
24966         //alert('setFrom hidden');
24967         this.setValue(this.el.dom.value);
24968     },
24969     
24970     onDestroy : function()
24971     {
24972         if(this.viewEl){
24973             Roo.get(this.viewEl).remove();
24974         }
24975          
24976         Roo.form.Checkbox.superclass.onDestroy.call(this);
24977     }
24978
24979 });/*
24980  * Based on:
24981  * Ext JS Library 1.1.1
24982  * Copyright(c) 2006-2007, Ext JS, LLC.
24983  *
24984  * Originally Released Under LGPL - original licence link has changed is not relivant.
24985  *
24986  * Fork - LGPL
24987  * <script type="text/javascript">
24988  */
24989  
24990 /**
24991  * @class Roo.form.Radio
24992  * @extends Roo.form.Checkbox
24993  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
24994  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
24995  * @constructor
24996  * Creates a new Radio
24997  * @param {Object} config Configuration options
24998  */
24999 Roo.form.Radio = function(){
25000     Roo.form.Radio.superclass.constructor.apply(this, arguments);
25001 };
25002 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
25003     inputType: 'radio',
25004
25005     /**
25006      * If this radio is part of a group, it will return the selected value
25007      * @return {String}
25008      */
25009     getGroupValue : function(){
25010         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
25011     }
25012 });//<script type="text/javascript">
25013
25014 /*
25015  * Ext JS Library 1.1.1
25016  * Copyright(c) 2006-2007, Ext JS, LLC.
25017  * licensing@extjs.com
25018  * 
25019  * http://www.extjs.com/license
25020  */
25021  
25022  /*
25023   * 
25024   * Known bugs:
25025   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
25026   * - IE ? - no idea how much works there.
25027   * 
25028   * 
25029   * 
25030   */
25031  
25032
25033 /**
25034  * @class Ext.form.HtmlEditor
25035  * @extends Ext.form.Field
25036  * Provides a lightweight HTML Editor component.
25037  *
25038  * This has been tested on Fireforx / Chrome.. IE may not be so great..
25039  * 
25040  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
25041  * supported by this editor.</b><br/><br/>
25042  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
25043  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25044  */
25045 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
25046       /**
25047      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25048      */
25049     toolbars : false,
25050     /**
25051      * @cfg {String} createLinkText The default text for the create link prompt
25052      */
25053     createLinkText : 'Please enter the URL for the link:',
25054     /**
25055      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
25056      */
25057     defaultLinkValue : 'http:/'+'/',
25058    
25059      /**
25060      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25061      *                        Roo.resizable.
25062      */
25063     resizable : false,
25064      /**
25065      * @cfg {Number} height (in pixels)
25066      */   
25067     height: 300,
25068    /**
25069      * @cfg {Number} width (in pixels)
25070      */   
25071     width: 500,
25072     
25073     /**
25074      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25075      * 
25076      */
25077     stylesheets: false,
25078     
25079     // id of frame..
25080     frameId: false,
25081     
25082     // private properties
25083     validationEvent : false,
25084     deferHeight: true,
25085     initialized : false,
25086     activated : false,
25087     sourceEditMode : false,
25088     onFocus : Roo.emptyFn,
25089     iframePad:3,
25090     hideMode:'offsets',
25091     
25092     defaultAutoCreate : { // modified by initCompnoent..
25093         tag: "textarea",
25094         style:"width:500px;height:300px;",
25095         autocomplete: "off"
25096     },
25097
25098     // private
25099     initComponent : function(){
25100         this.addEvents({
25101             /**
25102              * @event initialize
25103              * Fires when the editor is fully initialized (including the iframe)
25104              * @param {HtmlEditor} this
25105              */
25106             initialize: true,
25107             /**
25108              * @event activate
25109              * Fires when the editor is first receives the focus. Any insertion must wait
25110              * until after this event.
25111              * @param {HtmlEditor} this
25112              */
25113             activate: true,
25114              /**
25115              * @event beforesync
25116              * Fires before the textarea is updated with content from the editor iframe. Return false
25117              * to cancel the sync.
25118              * @param {HtmlEditor} this
25119              * @param {String} html
25120              */
25121             beforesync: true,
25122              /**
25123              * @event beforepush
25124              * Fires before the iframe editor is updated with content from the textarea. Return false
25125              * to cancel the push.
25126              * @param {HtmlEditor} this
25127              * @param {String} html
25128              */
25129             beforepush: true,
25130              /**
25131              * @event sync
25132              * Fires when the textarea is updated with content from the editor iframe.
25133              * @param {HtmlEditor} this
25134              * @param {String} html
25135              */
25136             sync: true,
25137              /**
25138              * @event push
25139              * Fires when the iframe editor is updated with content from the textarea.
25140              * @param {HtmlEditor} this
25141              * @param {String} html
25142              */
25143             push: true,
25144              /**
25145              * @event editmodechange
25146              * Fires when the editor switches edit modes
25147              * @param {HtmlEditor} this
25148              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25149              */
25150             editmodechange: true,
25151             /**
25152              * @event editorevent
25153              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25154              * @param {HtmlEditor} this
25155              */
25156             editorevent: true
25157         });
25158         this.defaultAutoCreate =  {
25159             tag: "textarea",
25160             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
25161             autocomplete: "off"
25162         };
25163     },
25164
25165     /**
25166      * Protected method that will not generally be called directly. It
25167      * is called when the editor creates its toolbar. Override this method if you need to
25168      * add custom toolbar buttons.
25169      * @param {HtmlEditor} editor
25170      */
25171     createToolbar : function(editor){
25172         if (!editor.toolbars || !editor.toolbars.length) {
25173             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
25174         }
25175         
25176         for (var i =0 ; i < editor.toolbars.length;i++) {
25177             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
25178             editor.toolbars[i].init(editor);
25179         }
25180          
25181         
25182     },
25183
25184     /**
25185      * Protected method that will not generally be called directly. It
25186      * is called when the editor initializes the iframe with HTML contents. Override this method if you
25187      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25188      */
25189     getDocMarkup : function(){
25190         // body styles..
25191         var st = '';
25192         if (this.stylesheets === false) {
25193             
25194             Roo.get(document.head).select('style').each(function(node) {
25195                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25196             });
25197             
25198             Roo.get(document.head).select('link').each(function(node) { 
25199                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25200             });
25201             
25202         } else if (!this.stylesheets.length) {
25203                 // simple..
25204                 st = '<style type="text/css">' +
25205                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25206                    '</style>';
25207         } else {
25208             Roo.each(this.stylesheets, function(s) {
25209                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
25210             });
25211             
25212         }
25213         
25214         st +=  '<style type="text/css">' +
25215             'IMG { cursor: pointer } ' +
25216         '</style>';
25217
25218         
25219         return '<html><head>' + st  +
25220             //<style type="text/css">' +
25221             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25222             //'</style>' +
25223             ' </head><body class="roo-htmleditor-body"></body></html>';
25224     },
25225
25226     // private
25227     onRender : function(ct, position)
25228     {
25229         var _t = this;
25230         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
25231         this.el.dom.style.border = '0 none';
25232         this.el.dom.setAttribute('tabIndex', -1);
25233         this.el.addClass('x-hidden');
25234         if(Roo.isIE){ // fix IE 1px bogus margin
25235             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25236         }
25237         this.wrap = this.el.wrap({
25238             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25239         });
25240         
25241         if (this.resizable) {
25242             this.resizeEl = new Roo.Resizable(this.wrap, {
25243                 pinned : true,
25244                 wrap: true,
25245                 dynamic : true,
25246                 minHeight : this.height,
25247                 height: this.height,
25248                 handles : this.resizable,
25249                 width: this.width,
25250                 listeners : {
25251                     resize : function(r, w, h) {
25252                         _t.onResize(w,h); // -something
25253                     }
25254                 }
25255             });
25256             
25257         }
25258
25259         this.frameId = Roo.id();
25260         
25261         this.createToolbar(this);
25262         
25263       
25264         
25265         var iframe = this.wrap.createChild({
25266             tag: 'iframe',
25267             id: this.frameId,
25268             name: this.frameId,
25269             frameBorder : 'no',
25270             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
25271         }, this.el
25272         );
25273         
25274        // console.log(iframe);
25275         //this.wrap.dom.appendChild(iframe);
25276
25277         this.iframe = iframe.dom;
25278
25279          this.assignDocWin();
25280         
25281         this.doc.designMode = 'on';
25282        
25283         this.doc.open();
25284         this.doc.write(this.getDocMarkup());
25285         this.doc.close();
25286
25287         
25288         var task = { // must defer to wait for browser to be ready
25289             run : function(){
25290                 //console.log("run task?" + this.doc.readyState);
25291                 this.assignDocWin();
25292                 if(this.doc.body || this.doc.readyState == 'complete'){
25293                     try {
25294                         this.doc.designMode="on";
25295                     } catch (e) {
25296                         return;
25297                     }
25298                     Roo.TaskMgr.stop(task);
25299                     this.initEditor.defer(10, this);
25300                 }
25301             },
25302             interval : 10,
25303             duration:10000,
25304             scope: this
25305         };
25306         Roo.TaskMgr.start(task);
25307
25308         if(!this.width){
25309             this.setSize(this.wrap.getSize());
25310         }
25311         if (this.resizeEl) {
25312             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25313             // should trigger onReize..
25314         }
25315     },
25316
25317     // private
25318     onResize : function(w, h)
25319     {
25320         //Roo.log('resize: ' +w + ',' + h );
25321         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
25322         if(this.el && this.iframe){
25323             if(typeof w == 'number'){
25324                 var aw = w - this.wrap.getFrameWidth('lr');
25325                 this.el.setWidth(this.adjustWidth('textarea', aw));
25326                 this.iframe.style.width = aw + 'px';
25327             }
25328             if(typeof h == 'number'){
25329                 var tbh = 0;
25330                 for (var i =0; i < this.toolbars.length;i++) {
25331                     // fixme - ask toolbars for heights?
25332                     tbh += this.toolbars[i].tb.el.getHeight();
25333                     if (this.toolbars[i].footer) {
25334                         tbh += this.toolbars[i].footer.el.getHeight();
25335                     }
25336                 }
25337                 
25338                 
25339                 
25340                 
25341                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25342                 ah -= 5; // knock a few pixes off for look..
25343                 this.el.setHeight(this.adjustWidth('textarea', ah));
25344                 this.iframe.style.height = ah + 'px';
25345                 if(this.doc){
25346                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
25347                 }
25348             }
25349         }
25350     },
25351
25352     /**
25353      * Toggles the editor between standard and source edit mode.
25354      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25355      */
25356     toggleSourceEdit : function(sourceEditMode){
25357         
25358         this.sourceEditMode = sourceEditMode === true;
25359         
25360         if(this.sourceEditMode){
25361           
25362             this.syncValue();
25363             this.iframe.className = 'x-hidden';
25364             this.el.removeClass('x-hidden');
25365             this.el.dom.removeAttribute('tabIndex');
25366             this.el.focus();
25367         }else{
25368              
25369             this.pushValue();
25370             this.iframe.className = '';
25371             this.el.addClass('x-hidden');
25372             this.el.dom.setAttribute('tabIndex', -1);
25373             this.deferFocus();
25374         }
25375         this.setSize(this.wrap.getSize());
25376         this.fireEvent('editmodechange', this, this.sourceEditMode);
25377     },
25378
25379     // private used internally
25380     createLink : function(){
25381         var url = prompt(this.createLinkText, this.defaultLinkValue);
25382         if(url && url != 'http:/'+'/'){
25383             this.relayCmd('createlink', url);
25384         }
25385     },
25386
25387     // private (for BoxComponent)
25388     adjustSize : Roo.BoxComponent.prototype.adjustSize,
25389
25390     // private (for BoxComponent)
25391     getResizeEl : function(){
25392         return this.wrap;
25393     },
25394
25395     // private (for BoxComponent)
25396     getPositionEl : function(){
25397         return this.wrap;
25398     },
25399
25400     // private
25401     initEvents : function(){
25402         this.originalValue = this.getValue();
25403     },
25404
25405     /**
25406      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25407      * @method
25408      */
25409     markInvalid : Roo.emptyFn,
25410     /**
25411      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25412      * @method
25413      */
25414     clearInvalid : Roo.emptyFn,
25415
25416     setValue : function(v){
25417         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
25418         this.pushValue();
25419     },
25420
25421     /**
25422      * Protected method that will not generally be called directly. If you need/want
25423      * custom HTML cleanup, this is the method you should override.
25424      * @param {String} html The HTML to be cleaned
25425      * return {String} The cleaned HTML
25426      */
25427     cleanHtml : function(html){
25428         html = String(html);
25429         if(html.length > 5){
25430             if(Roo.isSafari){ // strip safari nonsense
25431                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25432             }
25433         }
25434         if(html == '&nbsp;'){
25435             html = '';
25436         }
25437         return html;
25438     },
25439
25440     /**
25441      * Protected method that will not generally be called directly. Syncs the contents
25442      * of the editor iframe with the textarea.
25443      */
25444     syncValue : function(){
25445         if(this.initialized){
25446             var bd = (this.doc.body || this.doc.documentElement);
25447             //this.cleanUpPaste(); -- this is done else where and causes havoc..
25448             var html = bd.innerHTML;
25449             if(Roo.isSafari){
25450                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25451                 var m = bs.match(/text-align:(.*?);/i);
25452                 if(m && m[1]){
25453                     html = '<div style="'+m[0]+'">' + html + '</div>';
25454                 }
25455             }
25456             html = this.cleanHtml(html);
25457             // fix up the special chars..
25458             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
25459                 return "&#"+b.charCodeAt()+";" 
25460             });
25461             if(this.fireEvent('beforesync', this, html) !== false){
25462                 this.el.dom.value = html;
25463                 this.fireEvent('sync', this, html);
25464             }
25465         }
25466     },
25467
25468     /**
25469      * Protected method that will not generally be called directly. Pushes the value of the textarea
25470      * into the iframe editor.
25471      */
25472     pushValue : function(){
25473         if(this.initialized){
25474             var v = this.el.dom.value;
25475             if(v.length < 1){
25476                 v = '&#160;';
25477             }
25478             
25479             if(this.fireEvent('beforepush', this, v) !== false){
25480                 var d = (this.doc.body || this.doc.documentElement);
25481                 d.innerHTML = v;
25482                 this.cleanUpPaste();
25483                 this.el.dom.value = d.innerHTML;
25484                 this.fireEvent('push', this, v);
25485             }
25486         }
25487     },
25488
25489     // private
25490     deferFocus : function(){
25491         this.focus.defer(10, this);
25492     },
25493
25494     // doc'ed in Field
25495     focus : function(){
25496         if(this.win && !this.sourceEditMode){
25497             this.win.focus();
25498         }else{
25499             this.el.focus();
25500         }
25501     },
25502     
25503     assignDocWin: function()
25504     {
25505         var iframe = this.iframe;
25506         
25507          if(Roo.isIE){
25508             this.doc = iframe.contentWindow.document;
25509             this.win = iframe.contentWindow;
25510         } else {
25511             if (!Roo.get(this.frameId)) {
25512                 return;
25513             }
25514             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25515             this.win = Roo.get(this.frameId).dom.contentWindow;
25516         }
25517     },
25518     
25519     // private
25520     initEditor : function(){
25521         //console.log("INIT EDITOR");
25522         this.assignDocWin();
25523         
25524         
25525         
25526         this.doc.designMode="on";
25527         this.doc.open();
25528         this.doc.write(this.getDocMarkup());
25529         this.doc.close();
25530         
25531         var dbody = (this.doc.body || this.doc.documentElement);
25532         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25533         // this copies styles from the containing element into thsi one..
25534         // not sure why we need all of this..
25535         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25536         ss['background-attachment'] = 'fixed'; // w3c
25537         dbody.bgProperties = 'fixed'; // ie
25538         Roo.DomHelper.applyStyles(dbody, ss);
25539         Roo.EventManager.on(this.doc, {
25540             //'mousedown': this.onEditorEvent,
25541             'mouseup': this.onEditorEvent,
25542             'dblclick': this.onEditorEvent,
25543             'click': this.onEditorEvent,
25544             'keyup': this.onEditorEvent,
25545             buffer:100,
25546             scope: this
25547         });
25548         if(Roo.isGecko){
25549             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25550         }
25551         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25552             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25553         }
25554         this.initialized = true;
25555
25556         this.fireEvent('initialize', this);
25557         this.pushValue();
25558     },
25559
25560     // private
25561     onDestroy : function(){
25562         
25563         
25564         
25565         if(this.rendered){
25566             
25567             for (var i =0; i < this.toolbars.length;i++) {
25568                 // fixme - ask toolbars for heights?
25569                 this.toolbars[i].onDestroy();
25570             }
25571             
25572             this.wrap.dom.innerHTML = '';
25573             this.wrap.remove();
25574         }
25575     },
25576
25577     // private
25578     onFirstFocus : function(){
25579         
25580         this.assignDocWin();
25581         
25582         
25583         this.activated = true;
25584         for (var i =0; i < this.toolbars.length;i++) {
25585             this.toolbars[i].onFirstFocus();
25586         }
25587        
25588         if(Roo.isGecko){ // prevent silly gecko errors
25589             this.win.focus();
25590             var s = this.win.getSelection();
25591             if(!s.focusNode || s.focusNode.nodeType != 3){
25592                 var r = s.getRangeAt(0);
25593                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25594                 r.collapse(true);
25595                 this.deferFocus();
25596             }
25597             try{
25598                 this.execCmd('useCSS', true);
25599                 this.execCmd('styleWithCSS', false);
25600             }catch(e){}
25601         }
25602         this.fireEvent('activate', this);
25603     },
25604
25605     // private
25606     adjustFont: function(btn){
25607         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
25608         //if(Roo.isSafari){ // safari
25609         //    adjust *= 2;
25610        // }
25611         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
25612         if(Roo.isSafari){ // safari
25613             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
25614             v =  (v < 10) ? 10 : v;
25615             v =  (v > 48) ? 48 : v;
25616             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
25617             
25618         }
25619         
25620         
25621         v = Math.max(1, v+adjust);
25622         
25623         this.execCmd('FontSize', v  );
25624     },
25625
25626     onEditorEvent : function(e){
25627         this.fireEvent('editorevent', this, e);
25628       //  this.updateToolbar();
25629         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
25630     },
25631
25632     insertTag : function(tg)
25633     {
25634         // could be a bit smarter... -> wrap the current selected tRoo..
25635         
25636         this.execCmd("formatblock",   tg);
25637         
25638     },
25639     
25640     insertText : function(txt)
25641     {
25642         
25643         
25644         range = this.createRange();
25645         range.deleteContents();
25646                //alert(Sender.getAttribute('label'));
25647                
25648         range.insertNode(this.doc.createTextNode(txt));
25649     } ,
25650     
25651     // private
25652     relayBtnCmd : function(btn){
25653         this.relayCmd(btn.cmd);
25654     },
25655
25656     /**
25657      * Executes a Midas editor command on the editor document and performs necessary focus and
25658      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25659      * @param {String} cmd The Midas command
25660      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25661      */
25662     relayCmd : function(cmd, value){
25663         this.win.focus();
25664         this.execCmd(cmd, value);
25665         this.fireEvent('editorevent', this);
25666         //this.updateToolbar();
25667         this.deferFocus();
25668     },
25669
25670     /**
25671      * Executes a Midas editor command directly on the editor document.
25672      * For visual commands, you should use {@link #relayCmd} instead.
25673      * <b>This should only be called after the editor is initialized.</b>
25674      * @param {String} cmd The Midas command
25675      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25676      */
25677     execCmd : function(cmd, value){
25678         this.doc.execCommand(cmd, false, value === undefined ? null : value);
25679         this.syncValue();
25680     },
25681  
25682  
25683    
25684     /**
25685      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25686      * to insert tRoo.
25687      * @param {String} text | dom node.. 
25688      */
25689     insertAtCursor : function(text)
25690     {
25691         
25692         
25693         
25694         if(!this.activated){
25695             return;
25696         }
25697         /*
25698         if(Roo.isIE){
25699             this.win.focus();
25700             var r = this.doc.selection.createRange();
25701             if(r){
25702                 r.collapse(true);
25703                 r.pasteHTML(text);
25704                 this.syncValue();
25705                 this.deferFocus();
25706             
25707             }
25708             return;
25709         }
25710         */
25711         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25712             this.win.focus();
25713             
25714             
25715             // from jquery ui (MIT licenced)
25716             var range, node;
25717             var win = this.win;
25718             
25719             if (win.getSelection && win.getSelection().getRangeAt) {
25720                 range = win.getSelection().getRangeAt(0);
25721                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25722                 range.insertNode(node);
25723             } else if (win.document.selection && win.document.selection.createRange) {
25724                 // no firefox support
25725                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25726                 win.document.selection.createRange().pasteHTML(txt);
25727             } else {
25728                 // no firefox support
25729                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25730                 this.execCmd('InsertHTML', txt);
25731             } 
25732             
25733             this.syncValue();
25734             
25735             this.deferFocus();
25736         }
25737     },
25738  // private
25739     mozKeyPress : function(e){
25740         if(e.ctrlKey){
25741             var c = e.getCharCode(), cmd;
25742           
25743             if(c > 0){
25744                 c = String.fromCharCode(c).toLowerCase();
25745                 switch(c){
25746                     case 'b':
25747                         cmd = 'bold';
25748                         break;
25749                     case 'i':
25750                         cmd = 'italic';
25751                         break;
25752                     
25753                     case 'u':
25754                         cmd = 'underline';
25755                         break;
25756                     
25757                     case 'v':
25758                         this.cleanUpPaste.defer(100, this);
25759                         return;
25760                         
25761                 }
25762                 if(cmd){
25763                     this.win.focus();
25764                     this.execCmd(cmd);
25765                     this.deferFocus();
25766                     e.preventDefault();
25767                 }
25768                 
25769             }
25770         }
25771     },
25772
25773     // private
25774     fixKeys : function(){ // load time branching for fastest keydown performance
25775         if(Roo.isIE){
25776             return function(e){
25777                 var k = e.getKey(), r;
25778                 if(k == e.TAB){
25779                     e.stopEvent();
25780                     r = this.doc.selection.createRange();
25781                     if(r){
25782                         r.collapse(true);
25783                         r.pasteHTML('&#160;&#160;&#160;&#160;');
25784                         this.deferFocus();
25785                     }
25786                     return;
25787                 }
25788                 
25789                 if(k == e.ENTER){
25790                     r = this.doc.selection.createRange();
25791                     if(r){
25792                         var target = r.parentElement();
25793                         if(!target || target.tagName.toLowerCase() != 'li'){
25794                             e.stopEvent();
25795                             r.pasteHTML('<br />');
25796                             r.collapse(false);
25797                             r.select();
25798                         }
25799                     }
25800                 }
25801                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25802                     this.cleanUpPaste.defer(100, this);
25803                     return;
25804                 }
25805                 
25806                 
25807             };
25808         }else if(Roo.isOpera){
25809             return function(e){
25810                 var k = e.getKey();
25811                 if(k == e.TAB){
25812                     e.stopEvent();
25813                     this.win.focus();
25814                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
25815                     this.deferFocus();
25816                 }
25817                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25818                     this.cleanUpPaste.defer(100, this);
25819                     return;
25820                 }
25821                 
25822             };
25823         }else if(Roo.isSafari){
25824             return function(e){
25825                 var k = e.getKey();
25826                 
25827                 if(k == e.TAB){
25828                     e.stopEvent();
25829                     this.execCmd('InsertText','\t');
25830                     this.deferFocus();
25831                     return;
25832                 }
25833                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25834                     this.cleanUpPaste.defer(100, this);
25835                     return;
25836                 }
25837                 
25838              };
25839         }
25840     }(),
25841     
25842     getAllAncestors: function()
25843     {
25844         var p = this.getSelectedNode();
25845         var a = [];
25846         if (!p) {
25847             a.push(p); // push blank onto stack..
25848             p = this.getParentElement();
25849         }
25850         
25851         
25852         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25853             a.push(p);
25854             p = p.parentNode;
25855         }
25856         a.push(this.doc.body);
25857         return a;
25858     },
25859     lastSel : false,
25860     lastSelNode : false,
25861     
25862     
25863     getSelection : function() 
25864     {
25865         this.assignDocWin();
25866         return Roo.isIE ? this.doc.selection : this.win.getSelection();
25867     },
25868     
25869     getSelectedNode: function() 
25870     {
25871         // this may only work on Gecko!!!
25872         
25873         // should we cache this!!!!
25874         
25875         
25876         
25877          
25878         var range = this.createRange(this.getSelection()).cloneRange();
25879         
25880         if (Roo.isIE) {
25881             var parent = range.parentElement();
25882             while (true) {
25883                 var testRange = range.duplicate();
25884                 testRange.moveToElementText(parent);
25885                 if (testRange.inRange(range)) {
25886                     break;
25887                 }
25888                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25889                     break;
25890                 }
25891                 parent = parent.parentElement;
25892             }
25893             return parent;
25894         }
25895         
25896         // is ancestor a text element.
25897         var ac =  range.commonAncestorContainer;
25898         if (ac.nodeType == 3) {
25899             ac = ac.parentNode;
25900         }
25901         
25902         var ar = ac.childNodes;
25903          
25904         var nodes = [];
25905         var other_nodes = [];
25906         var has_other_nodes = false;
25907         for (var i=0;i<ar.length;i++) {
25908             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
25909                 continue;
25910             }
25911             // fullly contained node.
25912             
25913             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25914                 nodes.push(ar[i]);
25915                 continue;
25916             }
25917             
25918             // probably selected..
25919             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25920                 other_nodes.push(ar[i]);
25921                 continue;
25922             }
25923             // outer..
25924             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
25925                 continue;
25926             }
25927             
25928             
25929             has_other_nodes = true;
25930         }
25931         if (!nodes.length && other_nodes.length) {
25932             nodes= other_nodes;
25933         }
25934         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25935             return false;
25936         }
25937         
25938         return nodes[0];
25939     },
25940     createRange: function(sel)
25941     {
25942         // this has strange effects when using with 
25943         // top toolbar - not sure if it's a great idea.
25944         //this.editor.contentWindow.focus();
25945         if (typeof sel != "undefined") {
25946             try {
25947                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25948             } catch(e) {
25949                 return this.doc.createRange();
25950             }
25951         } else {
25952             return this.doc.createRange();
25953         }
25954     },
25955     getParentElement: function()
25956     {
25957         
25958         this.assignDocWin();
25959         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25960         
25961         var range = this.createRange(sel);
25962          
25963         try {
25964             var p = range.commonAncestorContainer;
25965             while (p.nodeType == 3) { // text node
25966                 p = p.parentNode;
25967             }
25968             return p;
25969         } catch (e) {
25970             return null;
25971         }
25972     
25973     },
25974     /***
25975      *
25976      * Range intersection.. the hard stuff...
25977      *  '-1' = before
25978      *  '0' = hits..
25979      *  '1' = after.
25980      *         [ -- selected range --- ]
25981      *   [fail]                        [fail]
25982      *
25983      *    basically..
25984      *      if end is before start or  hits it. fail.
25985      *      if start is after end or hits it fail.
25986      *
25987      *   if either hits (but other is outside. - then it's not 
25988      *   
25989      *    
25990      **/
25991     
25992     
25993     // @see http://www.thismuchiknow.co.uk/?p=64.
25994     rangeIntersectsNode : function(range, node)
25995     {
25996         var nodeRange = node.ownerDocument.createRange();
25997         try {
25998             nodeRange.selectNode(node);
25999         } catch (e) {
26000             nodeRange.selectNodeContents(node);
26001         }
26002     
26003         var rangeStartRange = range.cloneRange();
26004         rangeStartRange.collapse(true);
26005     
26006         var rangeEndRange = range.cloneRange();
26007         rangeEndRange.collapse(false);
26008     
26009         var nodeStartRange = nodeRange.cloneRange();
26010         nodeStartRange.collapse(true);
26011     
26012         var nodeEndRange = nodeRange.cloneRange();
26013         nodeEndRange.collapse(false);
26014     
26015         return rangeStartRange.compareBoundaryPoints(
26016                  Range.START_TO_START, nodeEndRange) == -1 &&
26017                rangeEndRange.compareBoundaryPoints(
26018                  Range.START_TO_START, nodeStartRange) == 1;
26019         
26020          
26021     },
26022     rangeCompareNode : function(range, node)
26023     {
26024         var nodeRange = node.ownerDocument.createRange();
26025         try {
26026             nodeRange.selectNode(node);
26027         } catch (e) {
26028             nodeRange.selectNodeContents(node);
26029         }
26030         
26031         
26032         range.collapse(true);
26033     
26034         nodeRange.collapse(true);
26035      
26036         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26037         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
26038          
26039         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26040         
26041         var nodeIsBefore   =  ss == 1;
26042         var nodeIsAfter    = ee == -1;
26043         
26044         if (nodeIsBefore && nodeIsAfter)
26045             return 0; // outer
26046         if (!nodeIsBefore && nodeIsAfter)
26047             return 1; //right trailed.
26048         
26049         if (nodeIsBefore && !nodeIsAfter)
26050             return 2;  // left trailed.
26051         // fully contined.
26052         return 3;
26053     },
26054
26055     // private? - in a new class?
26056     cleanUpPaste :  function()
26057     {
26058         // cleans up the whole document..
26059          Roo.log('cleanuppaste');
26060         this.cleanUpChildren(this.doc.body);
26061         var clean = this.cleanWordChars(this.doc.body.innerHTML);
26062         if (clean != this.doc.body.innerHTML) {
26063             this.doc.body.innerHTML = clean;
26064         }
26065         
26066     },
26067     
26068     cleanWordChars : function(input) {
26069         var he = Roo.form.HtmlEditor;
26070     
26071         var output = input;
26072         Roo.each(he.swapCodes, function(sw) { 
26073         
26074             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26075             output = output.replace(swapper, sw[1]);
26076         });
26077         return output;
26078     },
26079     
26080     
26081     cleanUpChildren : function (n)
26082     {
26083         if (!n.childNodes.length) {
26084             return;
26085         }
26086         for (var i = n.childNodes.length-1; i > -1 ; i--) {
26087            this.cleanUpChild(n.childNodes[i]);
26088         }
26089     },
26090     
26091     
26092         
26093     
26094     cleanUpChild : function (node)
26095     {
26096         //console.log(node);
26097         if (node.nodeName == "#text") {
26098             // clean up silly Windows -- stuff?
26099             return; 
26100         }
26101         if (node.nodeName == "#comment") {
26102             node.parentNode.removeChild(node);
26103             // clean up silly Windows -- stuff?
26104             return; 
26105         }
26106         
26107         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
26108             // remove node.
26109             node.parentNode.removeChild(node);
26110             return;
26111             
26112         }
26113         
26114         var remove_keep_children= Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1;
26115         
26116         // remove <a name=....> as rendering on yahoo mailer is bored with this.
26117         
26118         if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26119             remove_keep_children = true;
26120         }
26121         
26122         if (remove_keep_children) {
26123             this.cleanUpChildren(node);
26124             // inserts everything just before this node...
26125             while (node.childNodes.length) {
26126                 var cn = node.childNodes[0];
26127                 node.removeChild(cn);
26128                 node.parentNode.insertBefore(cn, node);
26129             }
26130             node.parentNode.removeChild(node);
26131             return;
26132         }
26133         
26134         if (!node.attributes || !node.attributes.length) {
26135             this.cleanUpChildren(node);
26136             return;
26137         }
26138         
26139         function cleanAttr(n,v)
26140         {
26141             
26142             if (v.match(/^\./) || v.match(/^\//)) {
26143                 return;
26144             }
26145             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
26146                 return;
26147             }
26148             if (v.match(/^#/)) {
26149                 return;
26150             }
26151             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
26152             node.removeAttribute(n);
26153             
26154         }
26155         
26156         function cleanStyle(n,v)
26157         {
26158             if (v.match(/expression/)) { //XSS?? should we even bother..
26159                 node.removeAttribute(n);
26160                 return;
26161             }
26162             
26163             
26164             var parts = v.split(/;/);
26165             Roo.each(parts, function(p) {
26166                 p = p.replace(/\s+/g,'');
26167                 if (!p.length) {
26168                     return true;
26169                 }
26170                 var l = p.split(':').shift().replace(/\s+/g,'');
26171                 
26172                 // only allow 'c whitelisted system attributes'
26173                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
26174                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
26175                     node.removeAttribute(n);
26176                     return false;
26177                 }
26178                 return true;
26179             });
26180             
26181             
26182         }
26183         
26184         
26185         for (var i = node.attributes.length-1; i > -1 ; i--) {
26186             var a = node.attributes[i];
26187             //console.log(a);
26188             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
26189                 node.removeAttribute(a.name);
26190                 continue;
26191             }
26192             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
26193                 cleanAttr(a.name,a.value); // fixme..
26194                 continue;
26195             }
26196             if (a.name == 'style') {
26197                 cleanStyle(a.name,a.value);
26198                 continue;
26199             }
26200             /// clean up MS crap..
26201             // tecnically this should be a list of valid class'es..
26202             
26203             
26204             if (a.name == 'class') {
26205                 if (a.value.match(/^Mso/)) {
26206                     node.className = '';
26207                 }
26208                 
26209                 if (a.value.match(/body/)) {
26210                     node.className = '';
26211                 }
26212                 continue;
26213             }
26214             
26215             // style cleanup!?
26216             // class cleanup?
26217             
26218         }
26219         
26220         
26221         this.cleanUpChildren(node);
26222         
26223         
26224     }
26225     
26226     
26227     // hide stuff that is not compatible
26228     /**
26229      * @event blur
26230      * @hide
26231      */
26232     /**
26233      * @event change
26234      * @hide
26235      */
26236     /**
26237      * @event focus
26238      * @hide
26239      */
26240     /**
26241      * @event specialkey
26242      * @hide
26243      */
26244     /**
26245      * @cfg {String} fieldClass @hide
26246      */
26247     /**
26248      * @cfg {String} focusClass @hide
26249      */
26250     /**
26251      * @cfg {String} autoCreate @hide
26252      */
26253     /**
26254      * @cfg {String} inputType @hide
26255      */
26256     /**
26257      * @cfg {String} invalidClass @hide
26258      */
26259     /**
26260      * @cfg {String} invalidText @hide
26261      */
26262     /**
26263      * @cfg {String} msgFx @hide
26264      */
26265     /**
26266      * @cfg {String} validateOnBlur @hide
26267      */
26268 });
26269
26270 Roo.form.HtmlEditor.white = [
26271         'area', 'br', 'img', 'input', 'hr', 'wbr',
26272         
26273        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
26274        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
26275        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
26276        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
26277        'table',   'ul',         'xmp', 
26278        
26279        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
26280       'thead',   'tr', 
26281      
26282       'dir', 'menu', 'ol', 'ul', 'dl',
26283        
26284       'embed',  'object'
26285 ];
26286
26287
26288 Roo.form.HtmlEditor.black = [
26289     //    'embed',  'object', // enable - backend responsiblity to clean thiese
26290         'applet', // 
26291         'base',   'basefont', 'bgsound', 'blink',  'body', 
26292         'frame',  'frameset', 'head',    'html',   'ilayer', 
26293         'iframe', 'layer',  'link',     'meta',    'object',   
26294         'script', 'style' ,'title',  'xml' // clean later..
26295 ];
26296 Roo.form.HtmlEditor.clean = [
26297     'script', 'style', 'title', 'xml'
26298 ];
26299 Roo.form.HtmlEditor.remove = [
26300     'font'
26301 ];
26302 // attributes..
26303
26304 Roo.form.HtmlEditor.ablack = [
26305     'on'
26306 ];
26307     
26308 Roo.form.HtmlEditor.aclean = [ 
26309     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
26310 ];
26311
26312 // protocols..
26313 Roo.form.HtmlEditor.pwhite= [
26314         'http',  'https',  'mailto'
26315 ];
26316
26317 // white listed style attributes.
26318 Roo.form.HtmlEditor.cwhite= [
26319         'text-align',
26320         'font-size'
26321 ];
26322
26323
26324 Roo.form.HtmlEditor.swapCodes   =[ 
26325     [    8211, "--" ], 
26326     [    8212, "--" ], 
26327     [    8216,  "'" ],  
26328     [    8217, "'" ],  
26329     [    8220, '"' ],  
26330     [    8221, '"' ],  
26331     [    8226, "*" ],  
26332     [    8230, "..." ]
26333 ]; 
26334
26335     // <script type="text/javascript">
26336 /*
26337  * Based on
26338  * Ext JS Library 1.1.1
26339  * Copyright(c) 2006-2007, Ext JS, LLC.
26340  *  
26341  
26342  */
26343
26344 /**
26345  * @class Roo.form.HtmlEditorToolbar1
26346  * Basic Toolbar
26347  * 
26348  * Usage:
26349  *
26350  new Roo.form.HtmlEditor({
26351     ....
26352     toolbars : [
26353         new Roo.form.HtmlEditorToolbar1({
26354             disable : { fonts: 1 , format: 1, ..., ... , ...],
26355             btns : [ .... ]
26356         })
26357     }
26358      
26359  * 
26360  * @cfg {Object} disable List of elements to disable..
26361  * @cfg {Array} btns List of additional buttons.
26362  * 
26363  * 
26364  * NEEDS Extra CSS? 
26365  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26366  */
26367  
26368 Roo.form.HtmlEditor.ToolbarStandard = function(config)
26369 {
26370     
26371     Roo.apply(this, config);
26372     
26373     // default disabled, based on 'good practice'..
26374     this.disable = this.disable || {};
26375     Roo.applyIf(this.disable, {
26376         fontSize : true,
26377         colors : true,
26378         specialElements : true
26379     });
26380     
26381     
26382     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26383     // dont call parent... till later.
26384 }
26385
26386 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
26387     
26388     tb: false,
26389     
26390     rendered: false,
26391     
26392     editor : false,
26393     /**
26394      * @cfg {Object} disable  List of toolbar elements to disable
26395          
26396      */
26397     disable : false,
26398       /**
26399      * @cfg {Array} fontFamilies An array of available font families
26400      */
26401     fontFamilies : [
26402         'Arial',
26403         'Courier New',
26404         'Tahoma',
26405         'Times New Roman',
26406         'Verdana'
26407     ],
26408     
26409     specialChars : [
26410            "&#169;",
26411           "&#174;",     
26412           "&#8482;",    
26413           "&#163;" ,    
26414          // "&#8212;",    
26415           "&#8230;",    
26416           "&#247;" ,    
26417         //  "&#225;" ,     ?? a acute?
26418            "&#8364;"    , //Euro
26419        //   "&#8220;"    ,
26420         //  "&#8221;"    ,
26421         //  "&#8226;"    ,
26422           "&#176;"  //   , // degrees
26423
26424          // "&#233;"     , // e ecute
26425          // "&#250;"     , // u ecute?
26426     ],
26427     
26428     specialElements : [
26429         {
26430             text: "Insert Table",
26431             xtype: 'MenuItem',
26432             xns : Roo.Menu,
26433             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
26434                 
26435         },
26436         {    
26437             text: "Insert Image",
26438             xtype: 'MenuItem',
26439             xns : Roo.Menu,
26440             ihtml : '<img src="about:blank"/>'
26441             
26442         }
26443         
26444          
26445     ],
26446     
26447     
26448     inputElements : [ 
26449             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
26450             "input:submit", "input:button", "select", "textarea", "label" ],
26451     formats : [
26452         ["p"] ,  
26453         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
26454         ["pre"],[ "code"], 
26455         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
26456     ],
26457      /**
26458      * @cfg {String} defaultFont default font to use.
26459      */
26460     defaultFont: 'tahoma',
26461    
26462     fontSelect : false,
26463     
26464     
26465     formatCombo : false,
26466     
26467     init : function(editor)
26468     {
26469         this.editor = editor;
26470         
26471         
26472         var fid = editor.frameId;
26473         var etb = this;
26474         function btn(id, toggle, handler){
26475             var xid = fid + '-'+ id ;
26476             return {
26477                 id : xid,
26478                 cmd : id,
26479                 cls : 'x-btn-icon x-edit-'+id,
26480                 enableToggle:toggle !== false,
26481                 scope: editor, // was editor...
26482                 handler:handler||editor.relayBtnCmd,
26483                 clickEvent:'mousedown',
26484                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
26485                 tabIndex:-1
26486             };
26487         }
26488         
26489         
26490         
26491         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
26492         this.tb = tb;
26493          // stop form submits
26494         tb.el.on('click', function(e){
26495             e.preventDefault(); // what does this do?
26496         });
26497
26498         if(!this.disable.font && !Roo.isSafari){
26499             /* why no safari for fonts
26500             editor.fontSelect = tb.el.createChild({
26501                 tag:'select',
26502                 tabIndex: -1,
26503                 cls:'x-font-select',
26504                 html: editor.createFontOptions()
26505             });
26506             editor.fontSelect.on('change', function(){
26507                 var font = editor.fontSelect.dom.value;
26508                 editor.relayCmd('fontname', font);
26509                 editor.deferFocus();
26510             }, editor);
26511             tb.add(
26512                 editor.fontSelect.dom,
26513                 '-'
26514             );
26515             */
26516         };
26517         if(!this.disable.formats){
26518             this.formatCombo = new Roo.form.ComboBox({
26519                 store: new Roo.data.SimpleStore({
26520                     id : 'tag',
26521                     fields: ['tag'],
26522                     data : this.formats // from states.js
26523                 }),
26524                 blockFocus : true,
26525                 //autoCreate : {tag: "div",  size: "20"},
26526                 displayField:'tag',
26527                 typeAhead: false,
26528                 mode: 'local',
26529                 editable : false,
26530                 triggerAction: 'all',
26531                 emptyText:'Add tag',
26532                 selectOnFocus:true,
26533                 width:135,
26534                 listeners : {
26535                     'select': function(c, r, i) {
26536                         editor.insertTag(r.get('tag'));
26537                         editor.focus();
26538                     }
26539                 }
26540
26541             });
26542             tb.addField(this.formatCombo);
26543             
26544         }
26545         
26546         if(!this.disable.format){
26547             tb.add(
26548                 btn('bold'),
26549                 btn('italic'),
26550                 btn('underline')
26551             );
26552         };
26553         if(!this.disable.fontSize){
26554             tb.add(
26555                 '-',
26556                 
26557                 
26558                 btn('increasefontsize', false, editor.adjustFont),
26559                 btn('decreasefontsize', false, editor.adjustFont)
26560             );
26561         };
26562         
26563         
26564         if(!this.disable.colors){
26565             tb.add(
26566                 '-', {
26567                     id:editor.frameId +'-forecolor',
26568                     cls:'x-btn-icon x-edit-forecolor',
26569                     clickEvent:'mousedown',
26570                     tooltip: this.buttonTips['forecolor'] || undefined,
26571                     tabIndex:-1,
26572                     menu : new Roo.menu.ColorMenu({
26573                         allowReselect: true,
26574                         focus: Roo.emptyFn,
26575                         value:'000000',
26576                         plain:true,
26577                         selectHandler: function(cp, color){
26578                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
26579                             editor.deferFocus();
26580                         },
26581                         scope: editor,
26582                         clickEvent:'mousedown'
26583                     })
26584                 }, {
26585                     id:editor.frameId +'backcolor',
26586                     cls:'x-btn-icon x-edit-backcolor',
26587                     clickEvent:'mousedown',
26588                     tooltip: this.buttonTips['backcolor'] || undefined,
26589                     tabIndex:-1,
26590                     menu : new Roo.menu.ColorMenu({
26591                         focus: Roo.emptyFn,
26592                         value:'FFFFFF',
26593                         plain:true,
26594                         allowReselect: true,
26595                         selectHandler: function(cp, color){
26596                             if(Roo.isGecko){
26597                                 editor.execCmd('useCSS', false);
26598                                 editor.execCmd('hilitecolor', color);
26599                                 editor.execCmd('useCSS', true);
26600                                 editor.deferFocus();
26601                             }else{
26602                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
26603                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
26604                                 editor.deferFocus();
26605                             }
26606                         },
26607                         scope:editor,
26608                         clickEvent:'mousedown'
26609                     })
26610                 }
26611             );
26612         };
26613         // now add all the items...
26614         
26615
26616         if(!this.disable.alignments){
26617             tb.add(
26618                 '-',
26619                 btn('justifyleft'),
26620                 btn('justifycenter'),
26621                 btn('justifyright')
26622             );
26623         };
26624
26625         //if(!Roo.isSafari){
26626             if(!this.disable.links){
26627                 tb.add(
26628                     '-',
26629                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
26630                 );
26631             };
26632
26633             if(!this.disable.lists){
26634                 tb.add(
26635                     '-',
26636                     btn('insertorderedlist'),
26637                     btn('insertunorderedlist')
26638                 );
26639             }
26640             if(!this.disable.sourceEdit){
26641                 tb.add(
26642                     '-',
26643                     btn('sourceedit', true, function(btn){
26644                         this.toggleSourceEdit(btn.pressed);
26645                     })
26646                 );
26647             }
26648         //}
26649         
26650         var smenu = { };
26651         // special menu.. - needs to be tidied up..
26652         if (!this.disable.special) {
26653             smenu = {
26654                 text: "&#169;",
26655                 cls: 'x-edit-none',
26656                 
26657                 menu : {
26658                     items : []
26659                 }
26660             };
26661             for (var i =0; i < this.specialChars.length; i++) {
26662                 smenu.menu.items.push({
26663                     
26664                     html: this.specialChars[i],
26665                     handler: function(a,b) {
26666                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
26667                         //editor.insertAtCursor(a.html);
26668                         
26669                     },
26670                     tabIndex:-1
26671                 });
26672             }
26673             
26674             
26675             tb.add(smenu);
26676             
26677             
26678         }
26679          
26680         if (!this.disable.specialElements) {
26681             var semenu = {
26682                 text: "Other;",
26683                 cls: 'x-edit-none',
26684                 menu : {
26685                     items : []
26686                 }
26687             };
26688             for (var i =0; i < this.specialElements.length; i++) {
26689                 semenu.menu.items.push(
26690                     Roo.apply({ 
26691                         handler: function(a,b) {
26692                             editor.insertAtCursor(this.ihtml);
26693                         }
26694                     }, this.specialElements[i])
26695                 );
26696                     
26697             }
26698             
26699             tb.add(semenu);
26700             
26701             
26702         }
26703          
26704         
26705         if (this.btns) {
26706             for(var i =0; i< this.btns.length;i++) {
26707                 var b = Roo.factory(this.btns[i],Roo.form);
26708                 b.cls =  'x-edit-none';
26709                 b.scope = editor;
26710                 tb.add(b);
26711             }
26712         
26713         }
26714         
26715         
26716         
26717         // disable everything...
26718         
26719         this.tb.items.each(function(item){
26720            if(item.id != editor.frameId+ '-sourceedit'){
26721                 item.disable();
26722             }
26723         });
26724         this.rendered = true;
26725         
26726         // the all the btns;
26727         editor.on('editorevent', this.updateToolbar, this);
26728         // other toolbars need to implement this..
26729         //editor.on('editmodechange', this.updateToolbar, this);
26730     },
26731     
26732     
26733     
26734     /**
26735      * Protected method that will not generally be called directly. It triggers
26736      * a toolbar update by reading the markup state of the current selection in the editor.
26737      */
26738     updateToolbar: function(){
26739
26740         if(!this.editor.activated){
26741             this.editor.onFirstFocus();
26742             return;
26743         }
26744
26745         var btns = this.tb.items.map, 
26746             doc = this.editor.doc,
26747             frameId = this.editor.frameId;
26748
26749         if(!this.disable.font && !Roo.isSafari){
26750             /*
26751             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
26752             if(name != this.fontSelect.dom.value){
26753                 this.fontSelect.dom.value = name;
26754             }
26755             */
26756         }
26757         if(!this.disable.format){
26758             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
26759             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
26760             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
26761         }
26762         if(!this.disable.alignments){
26763             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
26764             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
26765             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
26766         }
26767         if(!Roo.isSafari && !this.disable.lists){
26768             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
26769             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
26770         }
26771         
26772         var ans = this.editor.getAllAncestors();
26773         if (this.formatCombo) {
26774             
26775             
26776             var store = this.formatCombo.store;
26777             this.formatCombo.setValue("");
26778             for (var i =0; i < ans.length;i++) {
26779                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26780                     // select it..
26781                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26782                     break;
26783                 }
26784             }
26785         }
26786         
26787         
26788         
26789         // hides menus... - so this cant be on a menu...
26790         Roo.menu.MenuMgr.hideAll();
26791
26792         //this.editorsyncValue();
26793     },
26794    
26795     
26796     createFontOptions : function(){
26797         var buf = [], fs = this.fontFamilies, ff, lc;
26798         for(var i = 0, len = fs.length; i< len; i++){
26799             ff = fs[i];
26800             lc = ff.toLowerCase();
26801             buf.push(
26802                 '<option value="',lc,'" style="font-family:',ff,';"',
26803                     (this.defaultFont == lc ? ' selected="true">' : '>'),
26804                     ff,
26805                 '</option>'
26806             );
26807         }
26808         return buf.join('');
26809     },
26810     
26811     toggleSourceEdit : function(sourceEditMode){
26812         if(sourceEditMode === undefined){
26813             sourceEditMode = !this.sourceEditMode;
26814         }
26815         this.sourceEditMode = sourceEditMode === true;
26816         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
26817         // just toggle the button?
26818         if(btn.pressed !== this.editor.sourceEditMode){
26819             btn.toggle(this.editor.sourceEditMode);
26820             return;
26821         }
26822         
26823         if(this.sourceEditMode){
26824             this.tb.items.each(function(item){
26825                 if(item.cmd != 'sourceedit'){
26826                     item.disable();
26827                 }
26828             });
26829           
26830         }else{
26831             if(this.initialized){
26832                 this.tb.items.each(function(item){
26833                     item.enable();
26834                 });
26835             }
26836             
26837         }
26838         // tell the editor that it's been pressed..
26839         this.editor.toggleSourceEdit(sourceEditMode);
26840        
26841     },
26842      /**
26843      * Object collection of toolbar tooltips for the buttons in the editor. The key
26844      * is the command id associated with that button and the value is a valid QuickTips object.
26845      * For example:
26846 <pre><code>
26847 {
26848     bold : {
26849         title: 'Bold (Ctrl+B)',
26850         text: 'Make the selected text bold.',
26851         cls: 'x-html-editor-tip'
26852     },
26853     italic : {
26854         title: 'Italic (Ctrl+I)',
26855         text: 'Make the selected text italic.',
26856         cls: 'x-html-editor-tip'
26857     },
26858     ...
26859 </code></pre>
26860     * @type Object
26861      */
26862     buttonTips : {
26863         bold : {
26864             title: 'Bold (Ctrl+B)',
26865             text: 'Make the selected text bold.',
26866             cls: 'x-html-editor-tip'
26867         },
26868         italic : {
26869             title: 'Italic (Ctrl+I)',
26870             text: 'Make the selected text italic.',
26871             cls: 'x-html-editor-tip'
26872         },
26873         underline : {
26874             title: 'Underline (Ctrl+U)',
26875             text: 'Underline the selected text.',
26876             cls: 'x-html-editor-tip'
26877         },
26878         increasefontsize : {
26879             title: 'Grow Text',
26880             text: 'Increase the font size.',
26881             cls: 'x-html-editor-tip'
26882         },
26883         decreasefontsize : {
26884             title: 'Shrink Text',
26885             text: 'Decrease the font size.',
26886             cls: 'x-html-editor-tip'
26887         },
26888         backcolor : {
26889             title: 'Text Highlight Color',
26890             text: 'Change the background color of the selected text.',
26891             cls: 'x-html-editor-tip'
26892         },
26893         forecolor : {
26894             title: 'Font Color',
26895             text: 'Change the color of the selected text.',
26896             cls: 'x-html-editor-tip'
26897         },
26898         justifyleft : {
26899             title: 'Align Text Left',
26900             text: 'Align text to the left.',
26901             cls: 'x-html-editor-tip'
26902         },
26903         justifycenter : {
26904             title: 'Center Text',
26905             text: 'Center text in the editor.',
26906             cls: 'x-html-editor-tip'
26907         },
26908         justifyright : {
26909             title: 'Align Text Right',
26910             text: 'Align text to the right.',
26911             cls: 'x-html-editor-tip'
26912         },
26913         insertunorderedlist : {
26914             title: 'Bullet List',
26915             text: 'Start a bulleted list.',
26916             cls: 'x-html-editor-tip'
26917         },
26918         insertorderedlist : {
26919             title: 'Numbered List',
26920             text: 'Start a numbered list.',
26921             cls: 'x-html-editor-tip'
26922         },
26923         createlink : {
26924             title: 'Hyperlink',
26925             text: 'Make the selected text a hyperlink.',
26926             cls: 'x-html-editor-tip'
26927         },
26928         sourceedit : {
26929             title: 'Source Edit',
26930             text: 'Switch to source editing mode.',
26931             cls: 'x-html-editor-tip'
26932         }
26933     },
26934     // private
26935     onDestroy : function(){
26936         if(this.rendered){
26937             
26938             this.tb.items.each(function(item){
26939                 if(item.menu){
26940                     item.menu.removeAll();
26941                     if(item.menu.el){
26942                         item.menu.el.destroy();
26943                     }
26944                 }
26945                 item.destroy();
26946             });
26947              
26948         }
26949     },
26950     onFirstFocus: function() {
26951         this.tb.items.each(function(item){
26952            item.enable();
26953         });
26954     }
26955 });
26956
26957
26958
26959
26960 // <script type="text/javascript">
26961 /*
26962  * Based on
26963  * Ext JS Library 1.1.1
26964  * Copyright(c) 2006-2007, Ext JS, LLC.
26965  *  
26966  
26967  */
26968
26969  
26970 /**
26971  * @class Roo.form.HtmlEditor.ToolbarContext
26972  * Context Toolbar
26973  * 
26974  * Usage:
26975  *
26976  new Roo.form.HtmlEditor({
26977     ....
26978     toolbars : [
26979         { xtype: 'ToolbarStandard', styles : {} }
26980         { xtype: 'ToolbarContext', disable : {} }
26981     ]
26982 })
26983
26984      
26985  * 
26986  * @config : {Object} disable List of elements to disable.. (not done yet.)
26987  * @config : {Object} styles  Map of styles available.
26988  * 
26989  */
26990
26991 Roo.form.HtmlEditor.ToolbarContext = function(config)
26992 {
26993     
26994     Roo.apply(this, config);
26995     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26996     // dont call parent... till later.
26997     this.styles = this.styles || {};
26998 }
26999 Roo.form.HtmlEditor.ToolbarContext.types = {
27000     'IMG' : {
27001         width : {
27002             title: "Width",
27003             width: 40
27004         },
27005         height:  {
27006             title: "Height",
27007             width: 40
27008         },
27009         align: {
27010             title: "Align",
27011             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
27012             width : 80
27013             
27014         },
27015         border: {
27016             title: "Border",
27017             width: 40
27018         },
27019         alt: {
27020             title: "Alt",
27021             width: 120
27022         },
27023         src : {
27024             title: "Src",
27025             width: 220
27026         }
27027         
27028     },
27029     'A' : {
27030         name : {
27031             title: "Name",
27032             width: 50
27033         },
27034         href:  {
27035             title: "Href",
27036             width: 220
27037         } // border?
27038         
27039     },
27040     'TABLE' : {
27041         rows : {
27042             title: "Rows",
27043             width: 20
27044         },
27045         cols : {
27046             title: "Cols",
27047             width: 20
27048         },
27049         width : {
27050             title: "Width",
27051             width: 40
27052         },
27053         height : {
27054             title: "Height",
27055             width: 40
27056         },
27057         border : {
27058             title: "Border",
27059             width: 20
27060         }
27061     },
27062     'TD' : {
27063         width : {
27064             title: "Width",
27065             width: 40
27066         },
27067         height : {
27068             title: "Height",
27069             width: 40
27070         },   
27071         align: {
27072             title: "Align",
27073             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
27074             width: 80
27075         },
27076         valign: {
27077             title: "Valign",
27078             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
27079             width: 80
27080         },
27081         colspan: {
27082             title: "Colspan",
27083             width: 20
27084             
27085         }
27086     },
27087     'INPUT' : {
27088         name : {
27089             title: "name",
27090             width: 120
27091         },
27092         value : {
27093             title: "Value",
27094             width: 120
27095         },
27096         width : {
27097             title: "Width",
27098             width: 40
27099         }
27100     },
27101     'LABEL' : {
27102         'for' : {
27103             title: "For",
27104             width: 120
27105         }
27106     },
27107     'TEXTAREA' : {
27108           name : {
27109             title: "name",
27110             width: 120
27111         },
27112         rows : {
27113             title: "Rows",
27114             width: 20
27115         },
27116         cols : {
27117             title: "Cols",
27118             width: 20
27119         }
27120     },
27121     'SELECT' : {
27122         name : {
27123             title: "name",
27124             width: 120
27125         },
27126         selectoptions : {
27127             title: "Options",
27128             width: 200
27129         }
27130     },
27131     
27132     // should we really allow this??
27133     // should this just be 
27134     'BODY' : {
27135         title : {
27136             title: "title",
27137             width: 200,
27138             disabled : true
27139         }
27140     },
27141     '*' : {
27142         // empty..
27143     }
27144 };
27145
27146
27147
27148 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
27149     
27150     tb: false,
27151     
27152     rendered: false,
27153     
27154     editor : false,
27155     /**
27156      * @cfg {Object} disable  List of toolbar elements to disable
27157          
27158      */
27159     disable : false,
27160     /**
27161      * @cfg {Object} styles List of styles 
27162      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
27163      *
27164      * These must be defined in the page, so they get rendered correctly..
27165      * .headline { }
27166      * TD.underline { }
27167      * 
27168      */
27169     styles : false,
27170     
27171     
27172     
27173     toolbars : false,
27174     
27175     init : function(editor)
27176     {
27177         this.editor = editor;
27178         
27179         
27180         var fid = editor.frameId;
27181         var etb = this;
27182         function btn(id, toggle, handler){
27183             var xid = fid + '-'+ id ;
27184             return {
27185                 id : xid,
27186                 cmd : id,
27187                 cls : 'x-btn-icon x-edit-'+id,
27188                 enableToggle:toggle !== false,
27189                 scope: editor, // was editor...
27190                 handler:handler||editor.relayBtnCmd,
27191                 clickEvent:'mousedown',
27192                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27193                 tabIndex:-1
27194             };
27195         }
27196         // create a new element.
27197         var wdiv = editor.wrap.createChild({
27198                 tag: 'div'
27199             }, editor.wrap.dom.firstChild.nextSibling, true);
27200         
27201         // can we do this more than once??
27202         
27203          // stop form submits
27204       
27205  
27206         // disable everything...
27207         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
27208         this.toolbars = {};
27209            
27210         for (var i in  ty) {
27211           
27212             this.toolbars[i] = this.buildToolbar(ty[i],i);
27213         }
27214         this.tb = this.toolbars.BODY;
27215         this.tb.el.show();
27216         this.buildFooter();
27217         this.footer.show();
27218         editor.on('hide', function( ) { this.footer.hide() }, this);
27219         editor.on('show', function( ) { this.footer.show() }, this);
27220         
27221          
27222         this.rendered = true;
27223         
27224         // the all the btns;
27225         editor.on('editorevent', this.updateToolbar, this);
27226         // other toolbars need to implement this..
27227         //editor.on('editmodechange', this.updateToolbar, this);
27228     },
27229     
27230     
27231     
27232     /**
27233      * Protected method that will not generally be called directly. It triggers
27234      * a toolbar update by reading the markup state of the current selection in the editor.
27235      */
27236     updateToolbar: function(editor,ev,sel){
27237
27238         //Roo.log(ev);
27239         // capture mouse up - this is handy for selecting images..
27240         // perhaps should go somewhere else...
27241         if(!this.editor.activated){
27242              this.editor.onFirstFocus();
27243             return;
27244         }
27245         
27246         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
27247         // selectNode - might want to handle IE?
27248         if (ev &&
27249             (ev.type == 'mouseup' || ev.type == 'click' ) &&
27250             ev.target && ev.target.tagName == 'IMG') {
27251             // they have click on an image...
27252             // let's see if we can change the selection...
27253             sel = ev.target;
27254          
27255               var nodeRange = sel.ownerDocument.createRange();
27256             try {
27257                 nodeRange.selectNode(sel);
27258             } catch (e) {
27259                 nodeRange.selectNodeContents(sel);
27260             }
27261             //nodeRange.collapse(true);
27262             var s = editor.win.getSelection();
27263             s.removeAllRanges();
27264             s.addRange(nodeRange);
27265         }  
27266         
27267       
27268         var updateFooter = sel ? false : true;
27269         
27270         
27271         var ans = this.editor.getAllAncestors();
27272         
27273         // pick
27274         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
27275         
27276         if (!sel) { 
27277             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
27278             sel = sel ? sel : this.editor.doc.body;
27279             sel = sel.tagName.length ? sel : this.editor.doc.body;
27280             
27281         }
27282         // pick a menu that exists..
27283         var tn = sel.tagName.toUpperCase();
27284         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
27285         
27286         tn = sel.tagName.toUpperCase();
27287         
27288         var lastSel = this.tb.selectedNode
27289         
27290         this.tb.selectedNode = sel;
27291         
27292         // if current menu does not match..
27293         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
27294                 
27295             this.tb.el.hide();
27296             ///console.log("show: " + tn);
27297             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
27298             this.tb.el.show();
27299             // update name
27300             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
27301             
27302             
27303             // update attributes
27304             if (this.tb.fields) {
27305                 this.tb.fields.each(function(e) {
27306                    e.setValue(sel.getAttribute(e.attrname));
27307                 });
27308             }
27309             
27310             var hasStyles = false;
27311             for(var i in this.styles) {
27312                 hasStyles = true;
27313                 break;
27314             }
27315             
27316             // update styles
27317             if (hasStyles) { 
27318                 var st = this.tb.fields.item(0);
27319                 
27320                 st.store.removeAll();
27321                
27322                 
27323                 var cn = sel.className.split(/\s+/);
27324                 
27325                 var avs = [];
27326                 if (this.styles['*']) {
27327                     
27328                     Roo.each(this.styles['*'], function(v) {
27329                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
27330                     });
27331                 }
27332                 if (this.styles[tn]) { 
27333                     Roo.each(this.styles[tn], function(v) {
27334                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
27335                     });
27336                 }
27337                 
27338                 st.store.loadData(avs);
27339                 st.collapse();
27340                 st.setValue(cn);
27341             }
27342             // flag our selected Node.
27343             this.tb.selectedNode = sel;
27344            
27345            
27346             Roo.menu.MenuMgr.hideAll();
27347
27348         }
27349         
27350         if (!updateFooter) {
27351             return;
27352         }
27353         // update the footer
27354         //
27355         var html = '';
27356         
27357         this.footerEls = ans.reverse();
27358         Roo.each(this.footerEls, function(a,i) {
27359             if (!a) { return; }
27360             html += html.length ? ' &gt; '  :  '';
27361             
27362             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
27363             
27364         });
27365        
27366         // 
27367         var sz = this.footDisp.up('td').getSize();
27368         this.footDisp.dom.style.width = (sz.width -10) + 'px';
27369         this.footDisp.dom.style.marginLeft = '5px';
27370         
27371         this.footDisp.dom.style.overflow = 'hidden';
27372         
27373         this.footDisp.dom.innerHTML = html;
27374             
27375         //this.editorsyncValue();
27376     },
27377    
27378        
27379     // private
27380     onDestroy : function(){
27381         if(this.rendered){
27382             
27383             this.tb.items.each(function(item){
27384                 if(item.menu){
27385                     item.menu.removeAll();
27386                     if(item.menu.el){
27387                         item.menu.el.destroy();
27388                     }
27389                 }
27390                 item.destroy();
27391             });
27392              
27393         }
27394     },
27395     onFirstFocus: function() {
27396         // need to do this for all the toolbars..
27397         this.tb.items.each(function(item){
27398            item.enable();
27399         });
27400     },
27401     buildToolbar: function(tlist, nm)
27402     {
27403         var editor = this.editor;
27404          // create a new element.
27405         var wdiv = editor.wrap.createChild({
27406                 tag: 'div'
27407             }, editor.wrap.dom.firstChild.nextSibling, true);
27408         
27409        
27410         var tb = new Roo.Toolbar(wdiv);
27411         // add the name..
27412         
27413         tb.add(nm+ ":&nbsp;");
27414         
27415         var styles = [];
27416         for(var i in this.styles) {
27417             styles.push(i);
27418         }
27419         
27420         // styles...
27421         if (styles && styles.length) {
27422             
27423             // this needs a multi-select checkbox...
27424             tb.addField( new Roo.form.ComboBox({
27425                 store: new Roo.data.SimpleStore({
27426                     id : 'val',
27427                     fields: ['val', 'selected'],
27428                     data : [] 
27429                 }),
27430                 name : '-roo-edit-className',
27431                 attrname : 'className',
27432                 displayField:'val',
27433                 typeAhead: false,
27434                 mode: 'local',
27435                 editable : false,
27436                 triggerAction: 'all',
27437                 emptyText:'Select Style',
27438                 selectOnFocus:true,
27439                 width: 130,
27440                 listeners : {
27441                     'select': function(c, r, i) {
27442                         // initial support only for on class per el..
27443                         tb.selectedNode.className =  r ? r.get('val') : '';
27444                         editor.syncValue();
27445                     }
27446                 }
27447     
27448             }));
27449         }
27450             
27451         
27452         
27453         for (var i in tlist) {
27454             
27455             var item = tlist[i];
27456             tb.add(item.title + ":&nbsp;");
27457             
27458             
27459             
27460             
27461             if (item.opts) {
27462                 // opts == pulldown..
27463                 tb.addField( new Roo.form.ComboBox({
27464                     store: new Roo.data.SimpleStore({
27465                         id : 'val',
27466                         fields: ['val'],
27467                         data : item.opts  
27468                     }),
27469                     name : '-roo-edit-' + i,
27470                     attrname : i,
27471                     displayField:'val',
27472                     typeAhead: false,
27473                     mode: 'local',
27474                     editable : false,
27475                     triggerAction: 'all',
27476                     emptyText:'Select',
27477                     selectOnFocus:true,
27478                     width: item.width ? item.width  : 130,
27479                     listeners : {
27480                         'select': function(c, r, i) {
27481                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
27482                         }
27483                     }
27484
27485                 }));
27486                 continue;
27487                     
27488                  
27489                 
27490                 tb.addField( new Roo.form.TextField({
27491                     name: i,
27492                     width: 100,
27493                     //allowBlank:false,
27494                     value: ''
27495                 }));
27496                 continue;
27497             }
27498             tb.addField( new Roo.form.TextField({
27499                 name: '-roo-edit-' + i,
27500                 attrname : i,
27501                 
27502                 width: item.width,
27503                 //allowBlank:true,
27504                 value: '',
27505                 listeners: {
27506                     'change' : function(f, nv, ov) {
27507                         tb.selectedNode.setAttribute(f.attrname, nv);
27508                     }
27509                 }
27510             }));
27511              
27512         }
27513         tb.el.on('click', function(e){
27514             e.preventDefault(); // what does this do?
27515         });
27516         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
27517         tb.el.hide();
27518         tb.name = nm;
27519         // dont need to disable them... as they will get hidden
27520         return tb;
27521          
27522         
27523     },
27524     buildFooter : function()
27525     {
27526         
27527         var fel = this.editor.wrap.createChild();
27528         this.footer = new Roo.Toolbar(fel);
27529         // toolbar has scrolly on left / right?
27530         var footDisp= new Roo.Toolbar.Fill();
27531         var _t = this;
27532         this.footer.add(
27533             {
27534                 text : '&lt;',
27535                 xtype: 'Button',
27536                 handler : function() {
27537                     _t.footDisp.scrollTo('left',0,true)
27538                 }
27539             }
27540         );
27541         this.footer.add( footDisp );
27542         this.footer.add( 
27543             {
27544                 text : '&gt;',
27545                 xtype: 'Button',
27546                 handler : function() {
27547                     // no animation..
27548                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
27549                 }
27550             }
27551         );
27552         var fel = Roo.get(footDisp.el);
27553         fel.addClass('x-editor-context');
27554         this.footDispWrap = fel; 
27555         this.footDispWrap.overflow  = 'hidden';
27556         
27557         this.footDisp = fel.createChild();
27558         this.footDispWrap.on('click', this.onContextClick, this)
27559         
27560         
27561     },
27562     onContextClick : function (ev,dom)
27563     {
27564         ev.preventDefault();
27565         var  cn = dom.className;
27566         Roo.log(cn);
27567         if (!cn.match(/x-ed-loc-/)) {
27568             return;
27569         }
27570         var n = cn.split('-').pop();
27571         var ans = this.footerEls;
27572         var sel = ans[n];
27573         
27574          // pick
27575         var range = this.editor.createRange();
27576         
27577         range.selectNodeContents(sel);
27578         //range.selectNode(sel);
27579         
27580         
27581         var selection = this.editor.getSelection();
27582         selection.removeAllRanges();
27583         selection.addRange(range);
27584         
27585         
27586         
27587         this.updateToolbar(null, null, sel);
27588         
27589         
27590     }
27591     
27592     
27593     
27594     
27595     
27596 });
27597
27598
27599
27600
27601
27602 /*
27603  * Based on:
27604  * Ext JS Library 1.1.1
27605  * Copyright(c) 2006-2007, Ext JS, LLC.
27606  *
27607  * Originally Released Under LGPL - original licence link has changed is not relivant.
27608  *
27609  * Fork - LGPL
27610  * <script type="text/javascript">
27611  */
27612  
27613 /**
27614  * @class Roo.form.BasicForm
27615  * @extends Roo.util.Observable
27616  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
27617  * @constructor
27618  * @param {String/HTMLElement/Roo.Element} el The form element or its id
27619  * @param {Object} config Configuration options
27620  */
27621 Roo.form.BasicForm = function(el, config){
27622     this.allItems = [];
27623     this.childForms = [];
27624     Roo.apply(this, config);
27625     /*
27626      * The Roo.form.Field items in this form.
27627      * @type MixedCollection
27628      */
27629      
27630      
27631     this.items = new Roo.util.MixedCollection(false, function(o){
27632         return o.id || (o.id = Roo.id());
27633     });
27634     this.addEvents({
27635         /**
27636          * @event beforeaction
27637          * Fires before any action is performed. Return false to cancel the action.
27638          * @param {Form} this
27639          * @param {Action} action The action to be performed
27640          */
27641         beforeaction: true,
27642         /**
27643          * @event actionfailed
27644          * Fires when an action fails.
27645          * @param {Form} this
27646          * @param {Action} action The action that failed
27647          */
27648         actionfailed : true,
27649         /**
27650          * @event actioncomplete
27651          * Fires when an action is completed.
27652          * @param {Form} this
27653          * @param {Action} action The action that completed
27654          */
27655         actioncomplete : true
27656     });
27657     if(el){
27658         this.initEl(el);
27659     }
27660     Roo.form.BasicForm.superclass.constructor.call(this);
27661 };
27662
27663 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
27664     /**
27665      * @cfg {String} method
27666      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
27667      */
27668     /**
27669      * @cfg {DataReader} reader
27670      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
27671      * This is optional as there is built-in support for processing JSON.
27672      */
27673     /**
27674      * @cfg {DataReader} errorReader
27675      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
27676      * This is completely optional as there is built-in support for processing JSON.
27677      */
27678     /**
27679      * @cfg {String} url
27680      * The URL to use for form actions if one isn't supplied in the action options.
27681      */
27682     /**
27683      * @cfg {Boolean} fileUpload
27684      * Set to true if this form is a file upload.
27685      */
27686      
27687     /**
27688      * @cfg {Object} baseParams
27689      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
27690      */
27691      /**
27692      
27693     /**
27694      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
27695      */
27696     timeout: 30,
27697
27698     // private
27699     activeAction : null,
27700
27701     /**
27702      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
27703      * or setValues() data instead of when the form was first created.
27704      */
27705     trackResetOnLoad : false,
27706     
27707     
27708     /**
27709      * childForms - used for multi-tab forms
27710      * @type {Array}
27711      */
27712     childForms : false,
27713     
27714     /**
27715      * allItems - full list of fields.
27716      * @type {Array}
27717      */
27718     allItems : false,
27719     
27720     /**
27721      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
27722      * element by passing it or its id or mask the form itself by passing in true.
27723      * @type Mixed
27724      */
27725     waitMsgTarget : false,
27726
27727     // private
27728     initEl : function(el){
27729         this.el = Roo.get(el);
27730         this.id = this.el.id || Roo.id();
27731         this.el.on('submit', this.onSubmit, this);
27732         this.el.addClass('x-form');
27733     },
27734
27735     // private
27736     onSubmit : function(e){
27737         e.stopEvent();
27738     },
27739
27740     /**
27741      * Returns true if client-side validation on the form is successful.
27742      * @return Boolean
27743      */
27744     isValid : function(){
27745         var valid = true;
27746         this.items.each(function(f){
27747            if(!f.validate()){
27748                valid = false;
27749            }
27750         });
27751         return valid;
27752     },
27753
27754     /**
27755      * Returns true if any fields in this form have changed since their original load.
27756      * @return Boolean
27757      */
27758     isDirty : function(){
27759         var dirty = false;
27760         this.items.each(function(f){
27761            if(f.isDirty()){
27762                dirty = true;
27763                return false;
27764            }
27765         });
27766         return dirty;
27767     },
27768
27769     /**
27770      * Performs a predefined action (submit or load) or custom actions you define on this form.
27771      * @param {String} actionName The name of the action type
27772      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
27773      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
27774      * accept other config options):
27775      * <pre>
27776 Property          Type             Description
27777 ----------------  ---------------  ----------------------------------------------------------------------------------
27778 url               String           The url for the action (defaults to the form's url)
27779 method            String           The form method to use (defaults to the form's method, or POST if not defined)
27780 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
27781 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
27782                                    validate the form on the client (defaults to false)
27783      * </pre>
27784      * @return {BasicForm} this
27785      */
27786     doAction : function(action, options){
27787         if(typeof action == 'string'){
27788             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
27789         }
27790         if(this.fireEvent('beforeaction', this, action) !== false){
27791             this.beforeAction(action);
27792             action.run.defer(100, action);
27793         }
27794         return this;
27795     },
27796
27797     /**
27798      * Shortcut to do a submit action.
27799      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
27800      * @return {BasicForm} this
27801      */
27802     submit : function(options){
27803         this.doAction('submit', options);
27804         return this;
27805     },
27806
27807     /**
27808      * Shortcut to do a load action.
27809      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
27810      * @return {BasicForm} this
27811      */
27812     load : function(options){
27813         this.doAction('load', options);
27814         return this;
27815     },
27816
27817     /**
27818      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
27819      * @param {Record} record The record to edit
27820      * @return {BasicForm} this
27821      */
27822     updateRecord : function(record){
27823         record.beginEdit();
27824         var fs = record.fields;
27825         fs.each(function(f){
27826             var field = this.findField(f.name);
27827             if(field){
27828                 record.set(f.name, field.getValue());
27829             }
27830         }, this);
27831         record.endEdit();
27832         return this;
27833     },
27834
27835     /**
27836      * Loads an Roo.data.Record into this form.
27837      * @param {Record} record The record to load
27838      * @return {BasicForm} this
27839      */
27840     loadRecord : function(record){
27841         this.setValues(record.data);
27842         return this;
27843     },
27844
27845     // private
27846     beforeAction : function(action){
27847         var o = action.options;
27848         
27849        
27850         if(this.waitMsgTarget === true){
27851             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
27852         }else if(this.waitMsgTarget){
27853             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
27854             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
27855         }else {
27856             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
27857         }
27858          
27859     },
27860
27861     // private
27862     afterAction : function(action, success){
27863         this.activeAction = null;
27864         var o = action.options;
27865         
27866         if(this.waitMsgTarget === true){
27867             this.el.unmask();
27868         }else if(this.waitMsgTarget){
27869             this.waitMsgTarget.unmask();
27870         }else{
27871             Roo.MessageBox.updateProgress(1);
27872             Roo.MessageBox.hide();
27873         }
27874          
27875         if(success){
27876             if(o.reset){
27877                 this.reset();
27878             }
27879             Roo.callback(o.success, o.scope, [this, action]);
27880             this.fireEvent('actioncomplete', this, action);
27881             
27882         }else{
27883             
27884             // failure condition..
27885             // we have a scenario where updates need confirming.
27886             // eg. if a locking scenario exists..
27887             // we look for { errors : { needs_confirm : true }} in the response.
27888             if (
27889                 (typeof(action.result) != 'undefined')  &&
27890                 (typeof(action.result.errors) != 'undefined')  &&
27891                 (typeof(action.result.errors.needs_confirm) != 'undefined')
27892            ){
27893                 var _t = this;
27894                 Roo.MessageBox.confirm(
27895                     "Change requires confirmation",
27896                     action.result.errorMsg,
27897                     function(r) {
27898                         if (r != 'yes') {
27899                             return;
27900                         }
27901                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
27902                     }
27903                     
27904                 );
27905                 
27906                 
27907                 
27908                 return;
27909             }
27910             
27911             Roo.callback(o.failure, o.scope, [this, action]);
27912             // show an error message if no failed handler is set..
27913             if (!this.hasListener('actionfailed')) {
27914                 Roo.MessageBox.alert("Error",
27915                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
27916                         action.result.errorMsg :
27917                         "Saving Failed, please check your entries or try again"
27918                 );
27919             }
27920             
27921             this.fireEvent('actionfailed', this, action);
27922         }
27923         
27924     },
27925
27926     /**
27927      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
27928      * @param {String} id The value to search for
27929      * @return Field
27930      */
27931     findField : function(id){
27932         var field = this.items.get(id);
27933         if(!field){
27934             this.items.each(function(f){
27935                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
27936                     field = f;
27937                     return false;
27938                 }
27939             });
27940         }
27941         return field || null;
27942     },
27943
27944     /**
27945      * Add a secondary form to this one, 
27946      * Used to provide tabbed forms. One form is primary, with hidden values 
27947      * which mirror the elements from the other forms.
27948      * 
27949      * @param {Roo.form.Form} form to add.
27950      * 
27951      */
27952     addForm : function(form)
27953     {
27954        
27955         if (this.childForms.indexOf(form) > -1) {
27956             // already added..
27957             return;
27958         }
27959         this.childForms.push(form);
27960         var n = '';
27961         Roo.each(form.allItems, function (fe) {
27962             
27963             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
27964             if (this.findField(n)) { // already added..
27965                 return;
27966             }
27967             var add = new Roo.form.Hidden({
27968                 name : n
27969             });
27970             add.render(this.el);
27971             
27972             this.add( add );
27973         }, this);
27974         
27975     },
27976     /**
27977      * Mark fields in this form invalid in bulk.
27978      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
27979      * @return {BasicForm} this
27980      */
27981     markInvalid : function(errors){
27982         if(errors instanceof Array){
27983             for(var i = 0, len = errors.length; i < len; i++){
27984                 var fieldError = errors[i];
27985                 var f = this.findField(fieldError.id);
27986                 if(f){
27987                     f.markInvalid(fieldError.msg);
27988                 }
27989             }
27990         }else{
27991             var field, id;
27992             for(id in errors){
27993                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
27994                     field.markInvalid(errors[id]);
27995                 }
27996             }
27997         }
27998         Roo.each(this.childForms || [], function (f) {
27999             f.markInvalid(errors);
28000         });
28001         
28002         return this;
28003     },
28004
28005     /**
28006      * Set values for fields in this form in bulk.
28007      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
28008      * @return {BasicForm} this
28009      */
28010     setValues : function(values){
28011         if(values instanceof Array){ // array of objects
28012             for(var i = 0, len = values.length; i < len; i++){
28013                 var v = values[i];
28014                 var f = this.findField(v.id);
28015                 if(f){
28016                     f.setValue(v.value);
28017                     if(this.trackResetOnLoad){
28018                         f.originalValue = f.getValue();
28019                     }
28020                 }
28021             }
28022         }else{ // object hash
28023             var field, id;
28024             for(id in values){
28025                 if(typeof values[id] != 'function' && (field = this.findField(id))){
28026                     
28027                     if (field.setFromData && 
28028                         field.valueField && 
28029                         field.displayField &&
28030                         // combos' with local stores can 
28031                         // be queried via setValue()
28032                         // to set their value..
28033                         (field.store && !field.store.isLocal)
28034                         ) {
28035                         // it's a combo
28036                         var sd = { };
28037                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
28038                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
28039                         field.setFromData(sd);
28040                         
28041                     } else {
28042                         field.setValue(values[id]);
28043                     }
28044                     
28045                     
28046                     if(this.trackResetOnLoad){
28047                         field.originalValue = field.getValue();
28048                     }
28049                 }
28050             }
28051         }
28052          
28053         Roo.each(this.childForms || [], function (f) {
28054             f.setValues(values);
28055         });
28056                 
28057         return this;
28058     },
28059
28060     /**
28061      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
28062      * they are returned as an array.
28063      * @param {Boolean} asString
28064      * @return {Object}
28065      */
28066     getValues : function(asString){
28067         if (this.childForms) {
28068             // copy values from the child forms
28069             Roo.each(this.childForms, function (f) {
28070                 this.setValues(f.getValues());
28071             }, this);
28072         }
28073         
28074         
28075         
28076         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
28077         if(asString === true){
28078             return fs;
28079         }
28080         return Roo.urlDecode(fs);
28081     },
28082     
28083     /**
28084      * Returns the fields in this form as an object with key/value pairs. 
28085      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
28086      * @return {Object}
28087      */
28088     getFieldValues : function(with_hidden)
28089     {
28090         if (this.childForms) {
28091             // copy values from the child forms
28092             // should this call getFieldValues - probably not as we do not currently copy
28093             // hidden fields when we generate..
28094             Roo.each(this.childForms, function (f) {
28095                 this.setValues(f.getValues());
28096             }, this);
28097         }
28098         
28099         var ret = {};
28100         this.items.each(function(f){
28101             if (!f.getName()) {
28102                 return;
28103             }
28104             var v = f.getValue();
28105             // not sure if this supported any more..
28106             if ((typeof(v) == 'object') && f.getRawValue) {
28107                 v = f.getRawValue() ; // dates..
28108             }
28109             // combo boxes where name != hiddenName...
28110             if (f.name != f.getName()) {
28111                 ret[f.name] = f.getRawValue();
28112             }
28113             ret[f.getName()] = v;
28114         });
28115         
28116         return ret;
28117     },
28118
28119     /**
28120      * Clears all invalid messages in this form.
28121      * @return {BasicForm} this
28122      */
28123     clearInvalid : function(){
28124         this.items.each(function(f){
28125            f.clearInvalid();
28126         });
28127         
28128         Roo.each(this.childForms || [], function (f) {
28129             f.clearInvalid();
28130         });
28131         
28132         
28133         return this;
28134     },
28135
28136     /**
28137      * Resets this form.
28138      * @return {BasicForm} this
28139      */
28140     reset : function(){
28141         this.items.each(function(f){
28142             f.reset();
28143         });
28144         
28145         Roo.each(this.childForms || [], function (f) {
28146             f.reset();
28147         });
28148        
28149         
28150         return this;
28151     },
28152
28153     /**
28154      * Add Roo.form components to this form.
28155      * @param {Field} field1
28156      * @param {Field} field2 (optional)
28157      * @param {Field} etc (optional)
28158      * @return {BasicForm} this
28159      */
28160     add : function(){
28161         this.items.addAll(Array.prototype.slice.call(arguments, 0));
28162         return this;
28163     },
28164
28165
28166     /**
28167      * Removes a field from the items collection (does NOT remove its markup).
28168      * @param {Field} field
28169      * @return {BasicForm} this
28170      */
28171     remove : function(field){
28172         this.items.remove(field);
28173         return this;
28174     },
28175
28176     /**
28177      * Looks at the fields in this form, checks them for an id attribute,
28178      * and calls applyTo on the existing dom element with that id.
28179      * @return {BasicForm} this
28180      */
28181     render : function(){
28182         this.items.each(function(f){
28183             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
28184                 f.applyTo(f.id);
28185             }
28186         });
28187         return this;
28188     },
28189
28190     /**
28191      * Calls {@link Ext#apply} for all fields in this form with the passed object.
28192      * @param {Object} values
28193      * @return {BasicForm} this
28194      */
28195     applyToFields : function(o){
28196         this.items.each(function(f){
28197            Roo.apply(f, o);
28198         });
28199         return this;
28200     },
28201
28202     /**
28203      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
28204      * @param {Object} values
28205      * @return {BasicForm} this
28206      */
28207     applyIfToFields : function(o){
28208         this.items.each(function(f){
28209            Roo.applyIf(f, o);
28210         });
28211         return this;
28212     }
28213 });
28214
28215 // back compat
28216 Roo.BasicForm = Roo.form.BasicForm;/*
28217  * Based on:
28218  * Ext JS Library 1.1.1
28219  * Copyright(c) 2006-2007, Ext JS, LLC.
28220  *
28221  * Originally Released Under LGPL - original licence link has changed is not relivant.
28222  *
28223  * Fork - LGPL
28224  * <script type="text/javascript">
28225  */
28226
28227 /**
28228  * @class Roo.form.Form
28229  * @extends Roo.form.BasicForm
28230  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
28231  * @constructor
28232  * @param {Object} config Configuration options
28233  */
28234 Roo.form.Form = function(config){
28235     var xitems =  [];
28236     if (config.items) {
28237         xitems = config.items;
28238         delete config.items;
28239     }
28240    
28241     
28242     Roo.form.Form.superclass.constructor.call(this, null, config);
28243     this.url = this.url || this.action;
28244     if(!this.root){
28245         this.root = new Roo.form.Layout(Roo.applyIf({
28246             id: Roo.id()
28247         }, config));
28248     }
28249     this.active = this.root;
28250     /**
28251      * Array of all the buttons that have been added to this form via {@link addButton}
28252      * @type Array
28253      */
28254     this.buttons = [];
28255     this.allItems = [];
28256     this.addEvents({
28257         /**
28258          * @event clientvalidation
28259          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
28260          * @param {Form} this
28261          * @param {Boolean} valid true if the form has passed client-side validation
28262          */
28263         clientvalidation: true,
28264         /**
28265          * @event rendered
28266          * Fires when the form is rendered
28267          * @param {Roo.form.Form} form
28268          */
28269         rendered : true
28270     });
28271     
28272     if (this.progressUrl) {
28273             // push a hidden field onto the list of fields..
28274             this.addxtype( {
28275                     xns: Roo.form, 
28276                     xtype : 'Hidden', 
28277                     name : 'UPLOAD_IDENTIFIER' 
28278             });
28279         }
28280         
28281     
28282     Roo.each(xitems, this.addxtype, this);
28283     
28284     
28285     
28286 };
28287
28288 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
28289     /**
28290      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
28291      */
28292     /**
28293      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
28294      */
28295     /**
28296      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
28297      */
28298     buttonAlign:'center',
28299
28300     /**
28301      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
28302      */
28303     minButtonWidth:75,
28304
28305     /**
28306      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
28307      * This property cascades to child containers if not set.
28308      */
28309     labelAlign:'left',
28310
28311     /**
28312      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
28313      * fires a looping event with that state. This is required to bind buttons to the valid
28314      * state using the config value formBind:true on the button.
28315      */
28316     monitorValid : false,
28317
28318     /**
28319      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
28320      */
28321     monitorPoll : 200,
28322     
28323     /**
28324      * @cfg {String} progressUrl - Url to return progress data 
28325      */
28326     
28327     progressUrl : false,
28328   
28329     /**
28330      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
28331      * fields are added and the column is closed. If no fields are passed the column remains open
28332      * until end() is called.
28333      * @param {Object} config The config to pass to the column
28334      * @param {Field} field1 (optional)
28335      * @param {Field} field2 (optional)
28336      * @param {Field} etc (optional)
28337      * @return Column The column container object
28338      */
28339     column : function(c){
28340         var col = new Roo.form.Column(c);
28341         this.start(col);
28342         if(arguments.length > 1){ // duplicate code required because of Opera
28343             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28344             this.end();
28345         }
28346         return col;
28347     },
28348
28349     /**
28350      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
28351      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
28352      * until end() is called.
28353      * @param {Object} config The config to pass to the fieldset
28354      * @param {Field} field1 (optional)
28355      * @param {Field} field2 (optional)
28356      * @param {Field} etc (optional)
28357      * @return FieldSet The fieldset container object
28358      */
28359     fieldset : function(c){
28360         var fs = new Roo.form.FieldSet(c);
28361         this.start(fs);
28362         if(arguments.length > 1){ // duplicate code required because of Opera
28363             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28364             this.end();
28365         }
28366         return fs;
28367     },
28368
28369     /**
28370      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
28371      * fields are added and the container is closed. If no fields are passed the container remains open
28372      * until end() is called.
28373      * @param {Object} config The config to pass to the Layout
28374      * @param {Field} field1 (optional)
28375      * @param {Field} field2 (optional)
28376      * @param {Field} etc (optional)
28377      * @return Layout The container object
28378      */
28379     container : function(c){
28380         var l = new Roo.form.Layout(c);
28381         this.start(l);
28382         if(arguments.length > 1){ // duplicate code required because of Opera
28383             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28384             this.end();
28385         }
28386         return l;
28387     },
28388
28389     /**
28390      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
28391      * @param {Object} container A Roo.form.Layout or subclass of Layout
28392      * @return {Form} this
28393      */
28394     start : function(c){
28395         // cascade label info
28396         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
28397         this.active.stack.push(c);
28398         c.ownerCt = this.active;
28399         this.active = c;
28400         return this;
28401     },
28402
28403     /**
28404      * Closes the current open container
28405      * @return {Form} this
28406      */
28407     end : function(){
28408         if(this.active == this.root){
28409             return this;
28410         }
28411         this.active = this.active.ownerCt;
28412         return this;
28413     },
28414
28415     /**
28416      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
28417      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
28418      * as the label of the field.
28419      * @param {Field} field1
28420      * @param {Field} field2 (optional)
28421      * @param {Field} etc. (optional)
28422      * @return {Form} this
28423      */
28424     add : function(){
28425         this.active.stack.push.apply(this.active.stack, arguments);
28426         this.allItems.push.apply(this.allItems,arguments);
28427         var r = [];
28428         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
28429             if(a[i].isFormField){
28430                 r.push(a[i]);
28431             }
28432         }
28433         if(r.length > 0){
28434             Roo.form.Form.superclass.add.apply(this, r);
28435         }
28436         return this;
28437     },
28438     
28439
28440     
28441     
28442     
28443      /**
28444      * Find any element that has been added to a form, using it's ID or name
28445      * This can include framesets, columns etc. along with regular fields..
28446      * @param {String} id - id or name to find.
28447      
28448      * @return {Element} e - or false if nothing found.
28449      */
28450     findbyId : function(id)
28451     {
28452         var ret = false;
28453         if (!id) {
28454             return ret;
28455         }
28456         Roo.each(this.allItems, function(f){
28457             if (f.id == id || f.name == id ){
28458                 ret = f;
28459                 return false;
28460             }
28461         });
28462         return ret;
28463     },
28464
28465     
28466     
28467     /**
28468      * Render this form into the passed container. This should only be called once!
28469      * @param {String/HTMLElement/Element} container The element this component should be rendered into
28470      * @return {Form} this
28471      */
28472     render : function(ct)
28473     {
28474         
28475         
28476         
28477         ct = Roo.get(ct);
28478         var o = this.autoCreate || {
28479             tag: 'form',
28480             method : this.method || 'POST',
28481             id : this.id || Roo.id()
28482         };
28483         this.initEl(ct.createChild(o));
28484
28485         this.root.render(this.el);
28486         
28487        
28488              
28489         this.items.each(function(f){
28490             f.render('x-form-el-'+f.id);
28491         });
28492
28493         if(this.buttons.length > 0){
28494             // tables are required to maintain order and for correct IE layout
28495             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
28496                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
28497                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
28498             }}, null, true);
28499             var tr = tb.getElementsByTagName('tr')[0];
28500             for(var i = 0, len = this.buttons.length; i < len; i++) {
28501                 var b = this.buttons[i];
28502                 var td = document.createElement('td');
28503                 td.className = 'x-form-btn-td';
28504                 b.render(tr.appendChild(td));
28505             }
28506         }
28507         if(this.monitorValid){ // initialize after render
28508             this.startMonitoring();
28509         }
28510         this.fireEvent('rendered', this);
28511         return this;
28512     },
28513
28514     /**
28515      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
28516      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
28517      * object or a valid Roo.DomHelper element config
28518      * @param {Function} handler The function called when the button is clicked
28519      * @param {Object} scope (optional) The scope of the handler function
28520      * @return {Roo.Button}
28521      */
28522     addButton : function(config, handler, scope){
28523         var bc = {
28524             handler: handler,
28525             scope: scope,
28526             minWidth: this.minButtonWidth,
28527             hideParent:true
28528         };
28529         if(typeof config == "string"){
28530             bc.text = config;
28531         }else{
28532             Roo.apply(bc, config);
28533         }
28534         var btn = new Roo.Button(null, bc);
28535         this.buttons.push(btn);
28536         return btn;
28537     },
28538
28539      /**
28540      * Adds a series of form elements (using the xtype property as the factory method.
28541      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
28542      * @param {Object} config 
28543      */
28544     
28545     addxtype : function()
28546     {
28547         var ar = Array.prototype.slice.call(arguments, 0);
28548         var ret = false;
28549         for(var i = 0; i < ar.length; i++) {
28550             if (!ar[i]) {
28551                 continue; // skip -- if this happends something invalid got sent, we 
28552                 // should ignore it, as basically that interface element will not show up
28553                 // and that should be pretty obvious!!
28554             }
28555             
28556             if (Roo.form[ar[i].xtype]) {
28557                 ar[i].form = this;
28558                 var fe = Roo.factory(ar[i], Roo.form);
28559                 if (!ret) {
28560                     ret = fe;
28561                 }
28562                 fe.form = this;
28563                 if (fe.store) {
28564                     fe.store.form = this;
28565                 }
28566                 if (fe.isLayout) {  
28567                          
28568                     this.start(fe);
28569                     this.allItems.push(fe);
28570                     if (fe.items && fe.addxtype) {
28571                         fe.addxtype.apply(fe, fe.items);
28572                         delete fe.items;
28573                     }
28574                      this.end();
28575                     continue;
28576                 }
28577                 
28578                 
28579                  
28580                 this.add(fe);
28581               //  console.log('adding ' + ar[i].xtype);
28582             }
28583             if (ar[i].xtype == 'Button') {  
28584                 //console.log('adding button');
28585                 //console.log(ar[i]);
28586                 this.addButton(ar[i]);
28587                 this.allItems.push(fe);
28588                 continue;
28589             }
28590             
28591             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
28592                 alert('end is not supported on xtype any more, use items');
28593             //    this.end();
28594             //    //console.log('adding end');
28595             }
28596             
28597         }
28598         return ret;
28599     },
28600     
28601     /**
28602      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
28603      * option "monitorValid"
28604      */
28605     startMonitoring : function(){
28606         if(!this.bound){
28607             this.bound = true;
28608             Roo.TaskMgr.start({
28609                 run : this.bindHandler,
28610                 interval : this.monitorPoll || 200,
28611                 scope: this
28612             });
28613         }
28614     },
28615
28616     /**
28617      * Stops monitoring of the valid state of this form
28618      */
28619     stopMonitoring : function(){
28620         this.bound = false;
28621     },
28622
28623     // private
28624     bindHandler : function(){
28625         if(!this.bound){
28626             return false; // stops binding
28627         }
28628         var valid = true;
28629         this.items.each(function(f){
28630             if(!f.isValid(true)){
28631                 valid = false;
28632                 return false;
28633             }
28634         });
28635         for(var i = 0, len = this.buttons.length; i < len; i++){
28636             var btn = this.buttons[i];
28637             if(btn.formBind === true && btn.disabled === valid){
28638                 btn.setDisabled(!valid);
28639             }
28640         }
28641         this.fireEvent('clientvalidation', this, valid);
28642     }
28643     
28644     
28645     
28646     
28647     
28648     
28649     
28650     
28651 });
28652
28653
28654 // back compat
28655 Roo.Form = Roo.form.Form;
28656 /*
28657  * Based on:
28658  * Ext JS Library 1.1.1
28659  * Copyright(c) 2006-2007, Ext JS, LLC.
28660  *
28661  * Originally Released Under LGPL - original licence link has changed is not relivant.
28662  *
28663  * Fork - LGPL
28664  * <script type="text/javascript">
28665  */
28666  
28667  /**
28668  * @class Roo.form.Action
28669  * Internal Class used to handle form actions
28670  * @constructor
28671  * @param {Roo.form.BasicForm} el The form element or its id
28672  * @param {Object} config Configuration options
28673  */
28674  
28675  
28676 // define the action interface
28677 Roo.form.Action = function(form, options){
28678     this.form = form;
28679     this.options = options || {};
28680 };
28681 /**
28682  * Client Validation Failed
28683  * @const 
28684  */
28685 Roo.form.Action.CLIENT_INVALID = 'client';
28686 /**
28687  * Server Validation Failed
28688  * @const 
28689  */
28690  Roo.form.Action.SERVER_INVALID = 'server';
28691  /**
28692  * Connect to Server Failed
28693  * @const 
28694  */
28695 Roo.form.Action.CONNECT_FAILURE = 'connect';
28696 /**
28697  * Reading Data from Server Failed
28698  * @const 
28699  */
28700 Roo.form.Action.LOAD_FAILURE = 'load';
28701
28702 Roo.form.Action.prototype = {
28703     type : 'default',
28704     failureType : undefined,
28705     response : undefined,
28706     result : undefined,
28707
28708     // interface method
28709     run : function(options){
28710
28711     },
28712
28713     // interface method
28714     success : function(response){
28715
28716     },
28717
28718     // interface method
28719     handleResponse : function(response){
28720
28721     },
28722
28723     // default connection failure
28724     failure : function(response){
28725         
28726         this.response = response;
28727         this.failureType = Roo.form.Action.CONNECT_FAILURE;
28728         this.form.afterAction(this, false);
28729     },
28730
28731     processResponse : function(response){
28732         this.response = response;
28733         if(!response.responseText){
28734             return true;
28735         }
28736         this.result = this.handleResponse(response);
28737         return this.result;
28738     },
28739
28740     // utility functions used internally
28741     getUrl : function(appendParams){
28742         var url = this.options.url || this.form.url || this.form.el.dom.action;
28743         if(appendParams){
28744             var p = this.getParams();
28745             if(p){
28746                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
28747             }
28748         }
28749         return url;
28750     },
28751
28752     getMethod : function(){
28753         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
28754     },
28755
28756     getParams : function(){
28757         var bp = this.form.baseParams;
28758         var p = this.options.params;
28759         if(p){
28760             if(typeof p == "object"){
28761                 p = Roo.urlEncode(Roo.applyIf(p, bp));
28762             }else if(typeof p == 'string' && bp){
28763                 p += '&' + Roo.urlEncode(bp);
28764             }
28765         }else if(bp){
28766             p = Roo.urlEncode(bp);
28767         }
28768         return p;
28769     },
28770
28771     createCallback : function(){
28772         return {
28773             success: this.success,
28774             failure: this.failure,
28775             scope: this,
28776             timeout: (this.form.timeout*1000),
28777             upload: this.form.fileUpload ? this.success : undefined
28778         };
28779     }
28780 };
28781
28782 Roo.form.Action.Submit = function(form, options){
28783     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
28784 };
28785
28786 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
28787     type : 'submit',
28788
28789     haveProgress : false,
28790     uploadComplete : false,
28791     
28792     // uploadProgress indicator.
28793     uploadProgress : function()
28794     {
28795         if (!this.form.progressUrl) {
28796             return;
28797         }
28798         
28799         if (!this.haveProgress) {
28800             Roo.MessageBox.progress("Uploading", "Uploading");
28801         }
28802         if (this.uploadComplete) {
28803            Roo.MessageBox.hide();
28804            return;
28805         }
28806         
28807         this.haveProgress = true;
28808    
28809         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
28810         
28811         var c = new Roo.data.Connection();
28812         c.request({
28813             url : this.form.progressUrl,
28814             params: {
28815                 id : uid
28816             },
28817             method: 'GET',
28818             success : function(req){
28819                //console.log(data);
28820                 var rdata = false;
28821                 var edata;
28822                 try  {
28823                    rdata = Roo.decode(req.responseText)
28824                 } catch (e) {
28825                     Roo.log("Invalid data from server..");
28826                     Roo.log(edata);
28827                     return;
28828                 }
28829                 if (!rdata || !rdata.success) {
28830                     Roo.log(rdata);
28831                     Roo.MessageBox.alert(Roo.encode(rdata));
28832                     return;
28833                 }
28834                 var data = rdata.data;
28835                 
28836                 if (this.uploadComplete) {
28837                    Roo.MessageBox.hide();
28838                    return;
28839                 }
28840                    
28841                 if (data){
28842                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
28843                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
28844                     );
28845                 }
28846                 this.uploadProgress.defer(2000,this);
28847             },
28848        
28849             failure: function(data) {
28850                 Roo.log('progress url failed ');
28851                 Roo.log(data);
28852             },
28853             scope : this
28854         });
28855            
28856     },
28857     
28858     
28859     run : function()
28860     {
28861         // run get Values on the form, so it syncs any secondary forms.
28862         this.form.getValues();
28863         
28864         var o = this.options;
28865         var method = this.getMethod();
28866         var isPost = method == 'POST';
28867         if(o.clientValidation === false || this.form.isValid()){
28868             
28869             if (this.form.progressUrl) {
28870                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
28871                     (new Date() * 1) + '' + Math.random());
28872                     
28873             } 
28874             
28875             
28876             Roo.Ajax.request(Roo.apply(this.createCallback(), {
28877                 form:this.form.el.dom,
28878                 url:this.getUrl(!isPost),
28879                 method: method,
28880                 params:isPost ? this.getParams() : null,
28881                 isUpload: this.form.fileUpload
28882             }));
28883             
28884             this.uploadProgress();
28885
28886         }else if (o.clientValidation !== false){ // client validation failed
28887             this.failureType = Roo.form.Action.CLIENT_INVALID;
28888             this.form.afterAction(this, false);
28889         }
28890     },
28891
28892     success : function(response)
28893     {
28894         this.uploadComplete= true;
28895         if (this.haveProgress) {
28896             Roo.MessageBox.hide();
28897         }
28898         
28899         
28900         var result = this.processResponse(response);
28901         if(result === true || result.success){
28902             this.form.afterAction(this, true);
28903             return;
28904         }
28905         if(result.errors){
28906             this.form.markInvalid(result.errors);
28907             this.failureType = Roo.form.Action.SERVER_INVALID;
28908         }
28909         this.form.afterAction(this, false);
28910     },
28911     failure : function(response)
28912     {
28913         this.uploadComplete= true;
28914         if (this.haveProgress) {
28915             Roo.MessageBox.hide();
28916         }
28917         
28918         this.response = response;
28919         this.failureType = Roo.form.Action.CONNECT_FAILURE;
28920         this.form.afterAction(this, false);
28921     },
28922     
28923     handleResponse : function(response){
28924         if(this.form.errorReader){
28925             var rs = this.form.errorReader.read(response);
28926             var errors = [];
28927             if(rs.records){
28928                 for(var i = 0, len = rs.records.length; i < len; i++) {
28929                     var r = rs.records[i];
28930                     errors[i] = r.data;
28931                 }
28932             }
28933             if(errors.length < 1){
28934                 errors = null;
28935             }
28936             return {
28937                 success : rs.success,
28938                 errors : errors
28939             };
28940         }
28941         var ret = false;
28942         try {
28943             ret = Roo.decode(response.responseText);
28944         } catch (e) {
28945             ret = {
28946                 success: false,
28947                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
28948                 errors : []
28949             };
28950         }
28951         return ret;
28952         
28953     }
28954 });
28955
28956
28957 Roo.form.Action.Load = function(form, options){
28958     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
28959     this.reader = this.form.reader;
28960 };
28961
28962 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
28963     type : 'load',
28964
28965     run : function(){
28966         
28967         Roo.Ajax.request(Roo.apply(
28968                 this.createCallback(), {
28969                     method:this.getMethod(),
28970                     url:this.getUrl(false),
28971                     params:this.getParams()
28972         }));
28973     },
28974
28975     success : function(response){
28976         
28977         var result = this.processResponse(response);
28978         if(result === true || !result.success || !result.data){
28979             this.failureType = Roo.form.Action.LOAD_FAILURE;
28980             this.form.afterAction(this, false);
28981             return;
28982         }
28983         this.form.clearInvalid();
28984         this.form.setValues(result.data);
28985         this.form.afterAction(this, true);
28986     },
28987
28988     handleResponse : function(response){
28989         if(this.form.reader){
28990             var rs = this.form.reader.read(response);
28991             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
28992             return {
28993                 success : rs.success,
28994                 data : data
28995             };
28996         }
28997         return Roo.decode(response.responseText);
28998     }
28999 });
29000
29001 Roo.form.Action.ACTION_TYPES = {
29002     'load' : Roo.form.Action.Load,
29003     'submit' : Roo.form.Action.Submit
29004 };/*
29005  * Based on:
29006  * Ext JS Library 1.1.1
29007  * Copyright(c) 2006-2007, Ext JS, LLC.
29008  *
29009  * Originally Released Under LGPL - original licence link has changed is not relivant.
29010  *
29011  * Fork - LGPL
29012  * <script type="text/javascript">
29013  */
29014  
29015 /**
29016  * @class Roo.form.Layout
29017  * @extends Roo.Component
29018  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
29019  * @constructor
29020  * @param {Object} config Configuration options
29021  */
29022 Roo.form.Layout = function(config){
29023     var xitems = [];
29024     if (config.items) {
29025         xitems = config.items;
29026         delete config.items;
29027     }
29028     Roo.form.Layout.superclass.constructor.call(this, config);
29029     this.stack = [];
29030     Roo.each(xitems, this.addxtype, this);
29031      
29032 };
29033
29034 Roo.extend(Roo.form.Layout, Roo.Component, {
29035     /**
29036      * @cfg {String/Object} autoCreate
29037      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
29038      */
29039     /**
29040      * @cfg {String/Object/Function} style
29041      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
29042      * a function which returns such a specification.
29043      */
29044     /**
29045      * @cfg {String} labelAlign
29046      * Valid values are "left," "top" and "right" (defaults to "left")
29047      */
29048     /**
29049      * @cfg {Number} labelWidth
29050      * Fixed width in pixels of all field labels (defaults to undefined)
29051      */
29052     /**
29053      * @cfg {Boolean} clear
29054      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
29055      */
29056     clear : true,
29057     /**
29058      * @cfg {String} labelSeparator
29059      * The separator to use after field labels (defaults to ':')
29060      */
29061     labelSeparator : ':',
29062     /**
29063      * @cfg {Boolean} hideLabels
29064      * True to suppress the display of field labels in this layout (defaults to false)
29065      */
29066     hideLabels : false,
29067
29068     // private
29069     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
29070     
29071     isLayout : true,
29072     
29073     // private
29074     onRender : function(ct, position){
29075         if(this.el){ // from markup
29076             this.el = Roo.get(this.el);
29077         }else {  // generate
29078             var cfg = this.getAutoCreate();
29079             this.el = ct.createChild(cfg, position);
29080         }
29081         if(this.style){
29082             this.el.applyStyles(this.style);
29083         }
29084         if(this.labelAlign){
29085             this.el.addClass('x-form-label-'+this.labelAlign);
29086         }
29087         if(this.hideLabels){
29088             this.labelStyle = "display:none";
29089             this.elementStyle = "padding-left:0;";
29090         }else{
29091             if(typeof this.labelWidth == 'number'){
29092                 this.labelStyle = "width:"+this.labelWidth+"px;";
29093                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
29094             }
29095             if(this.labelAlign == 'top'){
29096                 this.labelStyle = "width:auto;";
29097                 this.elementStyle = "padding-left:0;";
29098             }
29099         }
29100         var stack = this.stack;
29101         var slen = stack.length;
29102         if(slen > 0){
29103             if(!this.fieldTpl){
29104                 var t = new Roo.Template(
29105                     '<div class="x-form-item {5}">',
29106                         '<label for="{0}" style="{2}">{1}{4}</label>',
29107                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
29108                         '</div>',
29109                     '</div><div class="x-form-clear-left"></div>'
29110                 );
29111                 t.disableFormats = true;
29112                 t.compile();
29113                 Roo.form.Layout.prototype.fieldTpl = t;
29114             }
29115             for(var i = 0; i < slen; i++) {
29116                 if(stack[i].isFormField){
29117                     this.renderField(stack[i]);
29118                 }else{
29119                     this.renderComponent(stack[i]);
29120                 }
29121             }
29122         }
29123         if(this.clear){
29124             this.el.createChild({cls:'x-form-clear'});
29125         }
29126     },
29127
29128     // private
29129     renderField : function(f){
29130         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
29131                f.id, //0
29132                f.fieldLabel, //1
29133                f.labelStyle||this.labelStyle||'', //2
29134                this.elementStyle||'', //3
29135                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
29136                f.itemCls||this.itemCls||''  //5
29137        ], true).getPrevSibling());
29138     },
29139
29140     // private
29141     renderComponent : function(c){
29142         c.render(c.isLayout ? this.el : this.el.createChild());    
29143     },
29144     /**
29145      * Adds a object form elements (using the xtype property as the factory method.)
29146      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
29147      * @param {Object} config 
29148      */
29149     addxtype : function(o)
29150     {
29151         // create the lement.
29152         o.form = this.form;
29153         var fe = Roo.factory(o, Roo.form);
29154         this.form.allItems.push(fe);
29155         this.stack.push(fe);
29156         
29157         if (fe.isFormField) {
29158             this.form.items.add(fe);
29159         }
29160          
29161         return fe;
29162     }
29163 });
29164
29165 /**
29166  * @class Roo.form.Column
29167  * @extends Roo.form.Layout
29168  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
29169  * @constructor
29170  * @param {Object} config Configuration options
29171  */
29172 Roo.form.Column = function(config){
29173     Roo.form.Column.superclass.constructor.call(this, config);
29174 };
29175
29176 Roo.extend(Roo.form.Column, Roo.form.Layout, {
29177     /**
29178      * @cfg {Number/String} width
29179      * The fixed width of the column in pixels or CSS value (defaults to "auto")
29180      */
29181     /**
29182      * @cfg {String/Object} autoCreate
29183      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
29184      */
29185
29186     // private
29187     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
29188
29189     // private
29190     onRender : function(ct, position){
29191         Roo.form.Column.superclass.onRender.call(this, ct, position);
29192         if(this.width){
29193             this.el.setWidth(this.width);
29194         }
29195     }
29196 });
29197
29198
29199 /**
29200  * @class Roo.form.Row
29201  * @extends Roo.form.Layout
29202  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
29203  * @constructor
29204  * @param {Object} config Configuration options
29205  */
29206
29207  
29208 Roo.form.Row = function(config){
29209     Roo.form.Row.superclass.constructor.call(this, config);
29210 };
29211  
29212 Roo.extend(Roo.form.Row, Roo.form.Layout, {
29213       /**
29214      * @cfg {Number/String} width
29215      * The fixed width of the column in pixels or CSS value (defaults to "auto")
29216      */
29217     /**
29218      * @cfg {Number/String} height
29219      * The fixed height of the column in pixels or CSS value (defaults to "auto")
29220      */
29221     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
29222     
29223     padWidth : 20,
29224     // private
29225     onRender : function(ct, position){
29226         //console.log('row render');
29227         if(!this.rowTpl){
29228             var t = new Roo.Template(
29229                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
29230                     '<label for="{0}" style="{2}">{1}{4}</label>',
29231                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
29232                     '</div>',
29233                 '</div>'
29234             );
29235             t.disableFormats = true;
29236             t.compile();
29237             Roo.form.Layout.prototype.rowTpl = t;
29238         }
29239         this.fieldTpl = this.rowTpl;
29240         
29241         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
29242         var labelWidth = 100;
29243         
29244         if ((this.labelAlign != 'top')) {
29245             if (typeof this.labelWidth == 'number') {
29246                 labelWidth = this.labelWidth
29247             }
29248             this.padWidth =  20 + labelWidth;
29249             
29250         }
29251         
29252         Roo.form.Column.superclass.onRender.call(this, ct, position);
29253         if(this.width){
29254             this.el.setWidth(this.width);
29255         }
29256         if(this.height){
29257             this.el.setHeight(this.height);
29258         }
29259     },
29260     
29261     // private
29262     renderField : function(f){
29263         f.fieldEl = this.fieldTpl.append(this.el, [
29264                f.id, f.fieldLabel,
29265                f.labelStyle||this.labelStyle||'',
29266                this.elementStyle||'',
29267                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
29268                f.itemCls||this.itemCls||'',
29269                f.width ? f.width + this.padWidth : 160 + this.padWidth
29270        ],true);
29271     }
29272 });
29273  
29274
29275 /**
29276  * @class Roo.form.FieldSet
29277  * @extends Roo.form.Layout
29278  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
29279  * @constructor
29280  * @param {Object} config Configuration options
29281  */
29282 Roo.form.FieldSet = function(config){
29283     Roo.form.FieldSet.superclass.constructor.call(this, config);
29284 };
29285
29286 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
29287     /**
29288      * @cfg {String} legend
29289      * The text to display as the legend for the FieldSet (defaults to '')
29290      */
29291     /**
29292      * @cfg {String/Object} autoCreate
29293      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
29294      */
29295
29296     // private
29297     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
29298
29299     // private
29300     onRender : function(ct, position){
29301         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
29302         if(this.legend){
29303             this.setLegend(this.legend);
29304         }
29305     },
29306
29307     // private
29308     setLegend : function(text){
29309         if(this.rendered){
29310             this.el.child('legend').update(text);
29311         }
29312     }
29313 });/*
29314  * Based on:
29315  * Ext JS Library 1.1.1
29316  * Copyright(c) 2006-2007, Ext JS, LLC.
29317  *
29318  * Originally Released Under LGPL - original licence link has changed is not relivant.
29319  *
29320  * Fork - LGPL
29321  * <script type="text/javascript">
29322  */
29323 /**
29324  * @class Roo.form.VTypes
29325  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
29326  * @singleton
29327  */
29328 Roo.form.VTypes = function(){
29329     // closure these in so they are only created once.
29330     var alpha = /^[a-zA-Z_]+$/;
29331     var alphanum = /^[a-zA-Z0-9_]+$/;
29332     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
29333     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
29334
29335     // All these messages and functions are configurable
29336     return {
29337         /**
29338          * The function used to validate email addresses
29339          * @param {String} value The email address
29340          */
29341         'email' : function(v){
29342             return email.test(v);
29343         },
29344         /**
29345          * The error text to display when the email validation function returns false
29346          * @type String
29347          */
29348         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
29349         /**
29350          * The keystroke filter mask to be applied on email input
29351          * @type RegExp
29352          */
29353         'emailMask' : /[a-z0-9_\.\-@]/i,
29354
29355         /**
29356          * The function used to validate URLs
29357          * @param {String} value The URL
29358          */
29359         'url' : function(v){
29360             return url.test(v);
29361         },
29362         /**
29363          * The error text to display when the url validation function returns false
29364          * @type String
29365          */
29366         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
29367         
29368         /**
29369          * The function used to validate alpha values
29370          * @param {String} value The value
29371          */
29372         'alpha' : function(v){
29373             return alpha.test(v);
29374         },
29375         /**
29376          * The error text to display when the alpha validation function returns false
29377          * @type String
29378          */
29379         'alphaText' : 'This field should only contain letters and _',
29380         /**
29381          * The keystroke filter mask to be applied on alpha input
29382          * @type RegExp
29383          */
29384         'alphaMask' : /[a-z_]/i,
29385
29386         /**
29387          * The function used to validate alphanumeric values
29388          * @param {String} value The value
29389          */
29390         'alphanum' : function(v){
29391             return alphanum.test(v);
29392         },
29393         /**
29394          * The error text to display when the alphanumeric validation function returns false
29395          * @type String
29396          */
29397         'alphanumText' : 'This field should only contain letters, numbers and _',
29398         /**
29399          * The keystroke filter mask to be applied on alphanumeric input
29400          * @type RegExp
29401          */
29402         'alphanumMask' : /[a-z0-9_]/i
29403     };
29404 }();//<script type="text/javascript">
29405
29406 /**
29407  * @class Roo.form.FCKeditor
29408  * @extends Roo.form.TextArea
29409  * Wrapper around the FCKEditor http://www.fckeditor.net
29410  * @constructor
29411  * Creates a new FCKeditor
29412  * @param {Object} config Configuration options
29413  */
29414 Roo.form.FCKeditor = function(config){
29415     Roo.form.FCKeditor.superclass.constructor.call(this, config);
29416     this.addEvents({
29417          /**
29418          * @event editorinit
29419          * Fired when the editor is initialized - you can add extra handlers here..
29420          * @param {FCKeditor} this
29421          * @param {Object} the FCK object.
29422          */
29423         editorinit : true
29424     });
29425     
29426     
29427 };
29428 Roo.form.FCKeditor.editors = { };
29429 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
29430 {
29431     //defaultAutoCreate : {
29432     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
29433     //},
29434     // private
29435     /**
29436      * @cfg {Object} fck options - see fck manual for details.
29437      */
29438     fckconfig : false,
29439     
29440     /**
29441      * @cfg {Object} fck toolbar set (Basic or Default)
29442      */
29443     toolbarSet : 'Basic',
29444     /**
29445      * @cfg {Object} fck BasePath
29446      */ 
29447     basePath : '/fckeditor/',
29448     
29449     
29450     frame : false,
29451     
29452     value : '',
29453     
29454    
29455     onRender : function(ct, position)
29456     {
29457         if(!this.el){
29458             this.defaultAutoCreate = {
29459                 tag: "textarea",
29460                 style:"width:300px;height:60px;",
29461                 autocomplete: "off"
29462             };
29463         }
29464         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
29465         /*
29466         if(this.grow){
29467             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
29468             if(this.preventScrollbars){
29469                 this.el.setStyle("overflow", "hidden");
29470             }
29471             this.el.setHeight(this.growMin);
29472         }
29473         */
29474         //console.log('onrender' + this.getId() );
29475         Roo.form.FCKeditor.editors[this.getId()] = this;
29476          
29477
29478         this.replaceTextarea() ;
29479         
29480     },
29481     
29482     getEditor : function() {
29483         return this.fckEditor;
29484     },
29485     /**
29486      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
29487      * @param {Mixed} value The value to set
29488      */
29489     
29490     
29491     setValue : function(value)
29492     {
29493         //console.log('setValue: ' + value);
29494         
29495         if(typeof(value) == 'undefined') { // not sure why this is happending...
29496             return;
29497         }
29498         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
29499         
29500         //if(!this.el || !this.getEditor()) {
29501         //    this.value = value;
29502             //this.setValue.defer(100,this,[value]);    
29503         //    return;
29504         //} 
29505         
29506         if(!this.getEditor()) {
29507             return;
29508         }
29509         
29510         this.getEditor().SetData(value);
29511         
29512         //
29513
29514     },
29515
29516     /**
29517      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
29518      * @return {Mixed} value The field value
29519      */
29520     getValue : function()
29521     {
29522         
29523         if (this.frame && this.frame.dom.style.display == 'none') {
29524             return Roo.form.FCKeditor.superclass.getValue.call(this);
29525         }
29526         
29527         if(!this.el || !this.getEditor()) {
29528            
29529            // this.getValue.defer(100,this); 
29530             return this.value;
29531         }
29532        
29533         
29534         var value=this.getEditor().GetData();
29535         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
29536         return Roo.form.FCKeditor.superclass.getValue.call(this);
29537         
29538
29539     },
29540
29541     /**
29542      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
29543      * @return {Mixed} value The field value
29544      */
29545     getRawValue : function()
29546     {
29547         if (this.frame && this.frame.dom.style.display == 'none') {
29548             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
29549         }
29550         
29551         if(!this.el || !this.getEditor()) {
29552             //this.getRawValue.defer(100,this); 
29553             return this.value;
29554             return;
29555         }
29556         
29557         
29558         
29559         var value=this.getEditor().GetData();
29560         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
29561         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
29562          
29563     },
29564     
29565     setSize : function(w,h) {
29566         
29567         
29568         
29569         //if (this.frame && this.frame.dom.style.display == 'none') {
29570         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
29571         //    return;
29572         //}
29573         //if(!this.el || !this.getEditor()) {
29574         //    this.setSize.defer(100,this, [w,h]); 
29575         //    return;
29576         //}
29577         
29578         
29579         
29580         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
29581         
29582         this.frame.dom.setAttribute('width', w);
29583         this.frame.dom.setAttribute('height', h);
29584         this.frame.setSize(w,h);
29585         
29586     },
29587     
29588     toggleSourceEdit : function(value) {
29589         
29590       
29591          
29592         this.el.dom.style.display = value ? '' : 'none';
29593         this.frame.dom.style.display = value ?  'none' : '';
29594         
29595     },
29596     
29597     
29598     focus: function(tag)
29599     {
29600         if (this.frame.dom.style.display == 'none') {
29601             return Roo.form.FCKeditor.superclass.focus.call(this);
29602         }
29603         if(!this.el || !this.getEditor()) {
29604             this.focus.defer(100,this, [tag]); 
29605             return;
29606         }
29607         
29608         
29609         
29610         
29611         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
29612         this.getEditor().Focus();
29613         if (tgs.length) {
29614             if (!this.getEditor().Selection.GetSelection()) {
29615                 this.focus.defer(100,this, [tag]); 
29616                 return;
29617             }
29618             
29619             
29620             var r = this.getEditor().EditorDocument.createRange();
29621             r.setStart(tgs[0],0);
29622             r.setEnd(tgs[0],0);
29623             this.getEditor().Selection.GetSelection().removeAllRanges();
29624             this.getEditor().Selection.GetSelection().addRange(r);
29625             this.getEditor().Focus();
29626         }
29627         
29628     },
29629     
29630     
29631     
29632     replaceTextarea : function()
29633     {
29634         if ( document.getElementById( this.getId() + '___Frame' ) )
29635             return ;
29636         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
29637         //{
29638             // We must check the elements firstly using the Id and then the name.
29639         var oTextarea = document.getElementById( this.getId() );
29640         
29641         var colElementsByName = document.getElementsByName( this.getId() ) ;
29642          
29643         oTextarea.style.display = 'none' ;
29644
29645         if ( oTextarea.tabIndex ) {            
29646             this.TabIndex = oTextarea.tabIndex ;
29647         }
29648         
29649         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
29650         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
29651         this.frame = Roo.get(this.getId() + '___Frame')
29652     },
29653     
29654     _getConfigHtml : function()
29655     {
29656         var sConfig = '' ;
29657
29658         for ( var o in this.fckconfig ) {
29659             sConfig += sConfig.length > 0  ? '&amp;' : '';
29660             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
29661         }
29662
29663         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
29664     },
29665     
29666     
29667     _getIFrameHtml : function()
29668     {
29669         var sFile = 'fckeditor.html' ;
29670         /* no idea what this is about..
29671         try
29672         {
29673             if ( (/fcksource=true/i).test( window.top.location.search ) )
29674                 sFile = 'fckeditor.original.html' ;
29675         }
29676         catch (e) { 
29677         */
29678
29679         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
29680         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
29681         
29682         
29683         var html = '<iframe id="' + this.getId() +
29684             '___Frame" src="' + sLink +
29685             '" width="' + this.width +
29686             '" height="' + this.height + '"' +
29687             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
29688             ' frameborder="0" scrolling="no"></iframe>' ;
29689
29690         return html ;
29691     },
29692     
29693     _insertHtmlBefore : function( html, element )
29694     {
29695         if ( element.insertAdjacentHTML )       {
29696             // IE
29697             element.insertAdjacentHTML( 'beforeBegin', html ) ;
29698         } else { // Gecko
29699             var oRange = document.createRange() ;
29700             oRange.setStartBefore( element ) ;
29701             var oFragment = oRange.createContextualFragment( html );
29702             element.parentNode.insertBefore( oFragment, element ) ;
29703         }
29704     }
29705     
29706     
29707   
29708     
29709     
29710     
29711     
29712
29713 });
29714
29715 //Roo.reg('fckeditor', Roo.form.FCKeditor);
29716
29717 function FCKeditor_OnComplete(editorInstance){
29718     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
29719     f.fckEditor = editorInstance;
29720     //console.log("loaded");
29721     f.fireEvent('editorinit', f, editorInstance);
29722
29723   
29724
29725  
29726
29727
29728
29729
29730
29731
29732
29733
29734
29735
29736
29737
29738
29739
29740
29741 //<script type="text/javascript">
29742 /**
29743  * @class Roo.form.GridField
29744  * @extends Roo.form.Field
29745  * Embed a grid (or editable grid into a form)
29746  * STATUS ALPHA
29747  * 
29748  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
29749  * it needs 
29750  * xgrid.store = Roo.data.Store
29751  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
29752  * xgrid.store.reader = Roo.data.JsonReader 
29753  * 
29754  * 
29755  * @constructor
29756  * Creates a new GridField
29757  * @param {Object} config Configuration options
29758  */
29759 Roo.form.GridField = function(config){
29760     Roo.form.GridField.superclass.constructor.call(this, config);
29761      
29762 };
29763
29764 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
29765     /**
29766      * @cfg {Number} width  - used to restrict width of grid..
29767      */
29768     width : 100,
29769     /**
29770      * @cfg {Number} height - used to restrict height of grid..
29771      */
29772     height : 50,
29773      /**
29774      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
29775          * 
29776          *}
29777      */
29778     xgrid : false, 
29779     /**
29780      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
29781      * {tag: "input", type: "checkbox", autocomplete: "off"})
29782      */
29783    // defaultAutoCreate : { tag: 'div' },
29784     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
29785     /**
29786      * @cfg {String} addTitle Text to include for adding a title.
29787      */
29788     addTitle : false,
29789     //
29790     onResize : function(){
29791         Roo.form.Field.superclass.onResize.apply(this, arguments);
29792     },
29793
29794     initEvents : function(){
29795         // Roo.form.Checkbox.superclass.initEvents.call(this);
29796         // has no events...
29797        
29798     },
29799
29800
29801     getResizeEl : function(){
29802         return this.wrap;
29803     },
29804
29805     getPositionEl : function(){
29806         return this.wrap;
29807     },
29808
29809     // private
29810     onRender : function(ct, position){
29811         
29812         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
29813         var style = this.style;
29814         delete this.style;
29815         
29816         Roo.form.GridField.superclass.onRender.call(this, ct, position);
29817         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
29818         this.viewEl = this.wrap.createChild({ tag: 'div' });
29819         if (style) {
29820             this.viewEl.applyStyles(style);
29821         }
29822         if (this.width) {
29823             this.viewEl.setWidth(this.width);
29824         }
29825         if (this.height) {
29826             this.viewEl.setHeight(this.height);
29827         }
29828         //if(this.inputValue !== undefined){
29829         //this.setValue(this.value);
29830         
29831         
29832         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
29833         
29834         
29835         this.grid.render();
29836         this.grid.getDataSource().on('remove', this.refreshValue, this);
29837         this.grid.getDataSource().on('update', this.refreshValue, this);
29838         this.grid.on('afteredit', this.refreshValue, this);
29839  
29840     },
29841      
29842     
29843     /**
29844      * Sets the value of the item. 
29845      * @param {String} either an object  or a string..
29846      */
29847     setValue : function(v){
29848         //this.value = v;
29849         v = v || []; // empty set..
29850         // this does not seem smart - it really only affects memoryproxy grids..
29851         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
29852             var ds = this.grid.getDataSource();
29853             // assumes a json reader..
29854             var data = {}
29855             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
29856             ds.loadData( data);
29857         }
29858         // clear selection so it does not get stale.
29859         if (this.grid.sm) { 
29860             this.grid.sm.clearSelections();
29861         }
29862         
29863         Roo.form.GridField.superclass.setValue.call(this, v);
29864         this.refreshValue();
29865         // should load data in the grid really....
29866     },
29867     
29868     // private
29869     refreshValue: function() {
29870          var val = [];
29871         this.grid.getDataSource().each(function(r) {
29872             val.push(r.data);
29873         });
29874         this.el.dom.value = Roo.encode(val);
29875     }
29876     
29877      
29878     
29879     
29880 });/*
29881  * Based on:
29882  * Ext JS Library 1.1.1
29883  * Copyright(c) 2006-2007, Ext JS, LLC.
29884  *
29885  * Originally Released Under LGPL - original licence link has changed is not relivant.
29886  *
29887  * Fork - LGPL
29888  * <script type="text/javascript">
29889  */
29890 /**
29891  * @class Roo.form.DisplayField
29892  * @extends Roo.form.Field
29893  * A generic Field to display non-editable data.
29894  * @constructor
29895  * Creates a new Display Field item.
29896  * @param {Object} config Configuration options
29897  */
29898 Roo.form.DisplayField = function(config){
29899     Roo.form.DisplayField.superclass.constructor.call(this, config);
29900     
29901 };
29902
29903 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
29904     inputType:      'hidden',
29905     allowBlank:     true,
29906     readOnly:         true,
29907     
29908  
29909     /**
29910      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
29911      */
29912     focusClass : undefined,
29913     /**
29914      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
29915      */
29916     fieldClass: 'x-form-field',
29917     
29918      /**
29919      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
29920      */
29921     valueRenderer: undefined,
29922     
29923     width: 100,
29924     /**
29925      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
29926      * {tag: "input", type: "checkbox", autocomplete: "off"})
29927      */
29928      
29929  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
29930
29931     onResize : function(){
29932         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
29933         
29934     },
29935
29936     initEvents : function(){
29937         // Roo.form.Checkbox.superclass.initEvents.call(this);
29938         // has no events...
29939        
29940     },
29941
29942
29943     getResizeEl : function(){
29944         return this.wrap;
29945     },
29946
29947     getPositionEl : function(){
29948         return this.wrap;
29949     },
29950
29951     // private
29952     onRender : function(ct, position){
29953         
29954         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
29955         //if(this.inputValue !== undefined){
29956         this.wrap = this.el.wrap();
29957         
29958         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
29959         
29960         if (this.bodyStyle) {
29961             this.viewEl.applyStyles(this.bodyStyle);
29962         }
29963         //this.viewEl.setStyle('padding', '2px');
29964         
29965         this.setValue(this.value);
29966         
29967     },
29968 /*
29969     // private
29970     initValue : Roo.emptyFn,
29971
29972   */
29973
29974         // private
29975     onClick : function(){
29976         
29977     },
29978
29979     /**
29980      * Sets the checked state of the checkbox.
29981      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
29982      */
29983     setValue : function(v){
29984         this.value = v;
29985         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
29986         // this might be called before we have a dom element..
29987         if (!this.viewEl) {
29988             return;
29989         }
29990         this.viewEl.dom.innerHTML = html;
29991         Roo.form.DisplayField.superclass.setValue.call(this, v);
29992
29993     }
29994 });/*
29995  * 
29996  * Licence- LGPL
29997  * 
29998  */
29999
30000 /**
30001  * @class Roo.form.DayPicker
30002  * @extends Roo.form.Field
30003  * A Day picker show [M] [T] [W] ....
30004  * @constructor
30005  * Creates a new Day Picker
30006  * @param {Object} config Configuration options
30007  */
30008 Roo.form.DayPicker= function(config){
30009     Roo.form.DayPicker.superclass.constructor.call(this, config);
30010      
30011 };
30012
30013 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
30014     /**
30015      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30016      */
30017     focusClass : undefined,
30018     /**
30019      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30020      */
30021     fieldClass: "x-form-field",
30022    
30023     /**
30024      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30025      * {tag: "input", type: "checkbox", autocomplete: "off"})
30026      */
30027     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
30028     
30029    
30030     actionMode : 'viewEl', 
30031     //
30032     // private
30033  
30034     inputType : 'hidden',
30035     
30036      
30037     inputElement: false, // real input element?
30038     basedOn: false, // ????
30039     
30040     isFormField: true, // not sure where this is needed!!!!
30041
30042     onResize : function(){
30043         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
30044         if(!this.boxLabel){
30045             this.el.alignTo(this.wrap, 'c-c');
30046         }
30047     },
30048
30049     initEvents : function(){
30050         Roo.form.Checkbox.superclass.initEvents.call(this);
30051         this.el.on("click", this.onClick,  this);
30052         this.el.on("change", this.onClick,  this);
30053     },
30054
30055
30056     getResizeEl : function(){
30057         return this.wrap;
30058     },
30059
30060     getPositionEl : function(){
30061         return this.wrap;
30062     },
30063
30064     
30065     // private
30066     onRender : function(ct, position){
30067         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
30068        
30069         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
30070         
30071         var r1 = '<table><tr>';
30072         var r2 = '<tr class="x-form-daypick-icons">';
30073         for (var i=0; i < 7; i++) {
30074             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
30075             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
30076         }
30077         
30078         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
30079         viewEl.select('img').on('click', this.onClick, this);
30080         this.viewEl = viewEl;   
30081         
30082         
30083         // this will not work on Chrome!!!
30084         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
30085         this.el.on('propertychange', this.setFromHidden,  this);  //ie
30086         
30087         
30088           
30089
30090     },
30091
30092     // private
30093     initValue : Roo.emptyFn,
30094
30095     /**
30096      * Returns the checked state of the checkbox.
30097      * @return {Boolean} True if checked, else false
30098      */
30099     getValue : function(){
30100         return this.el.dom.value;
30101         
30102     },
30103
30104         // private
30105     onClick : function(e){ 
30106         //this.setChecked(!this.checked);
30107         Roo.get(e.target).toggleClass('x-menu-item-checked');
30108         this.refreshValue();
30109         //if(this.el.dom.checked != this.checked){
30110         //    this.setValue(this.el.dom.checked);
30111        // }
30112     },
30113     
30114     // private
30115     refreshValue : function()
30116     {
30117         var val = '';
30118         this.viewEl.select('img',true).each(function(e,i,n)  {
30119             val += e.is(".x-menu-item-checked") ? String(n) : '';
30120         });
30121         this.setValue(val, true);
30122     },
30123
30124     /**
30125      * Sets the checked state of the checkbox.
30126      * On is always based on a string comparison between inputValue and the param.
30127      * @param {Boolean/String} value - the value to set 
30128      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
30129      */
30130     setValue : function(v,suppressEvent){
30131         if (!this.el.dom) {
30132             return;
30133         }
30134         var old = this.el.dom.value ;
30135         this.el.dom.value = v;
30136         if (suppressEvent) {
30137             return ;
30138         }
30139          
30140         // update display..
30141         this.viewEl.select('img',true).each(function(e,i,n)  {
30142             
30143             var on = e.is(".x-menu-item-checked");
30144             var newv = v.indexOf(String(n)) > -1;
30145             if (on != newv) {
30146                 e.toggleClass('x-menu-item-checked');
30147             }
30148             
30149         });
30150         
30151         
30152         this.fireEvent('change', this, v, old);
30153         
30154         
30155     },
30156    
30157     // handle setting of hidden value by some other method!!?!?
30158     setFromHidden: function()
30159     {
30160         if(!this.el){
30161             return;
30162         }
30163         //console.log("SET FROM HIDDEN");
30164         //alert('setFrom hidden');
30165         this.setValue(this.el.dom.value);
30166     },
30167     
30168     onDestroy : function()
30169     {
30170         if(this.viewEl){
30171             Roo.get(this.viewEl).remove();
30172         }
30173          
30174         Roo.form.DayPicker.superclass.onDestroy.call(this);
30175     }
30176
30177 });/*
30178  * RooJS Library 1.1.1
30179  * Copyright(c) 2008-2011  Alan Knowles
30180  *
30181  * License - LGPL
30182  */
30183  
30184
30185 /**
30186  * @class Roo.form.ComboCheck
30187  * @extends Roo.form.ComboBox
30188  * A combobox for multiple select items.
30189  *
30190  * FIXME - could do with a reset button..
30191  * 
30192  * @constructor
30193  * Create a new ComboCheck
30194  * @param {Object} config Configuration options
30195  */
30196 Roo.form.ComboCheck = function(config){
30197     Roo.form.ComboCheck.superclass.constructor.call(this, config);
30198     // should verify some data...
30199     // like
30200     // hiddenName = required..
30201     // displayField = required
30202     // valudField == required
30203     var req= [ 'hiddenName', 'displayField', 'valueField' ];
30204     var _t = this;
30205     Roo.each(req, function(e) {
30206         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
30207             throw "Roo.form.ComboCheck : missing value for: " + e;
30208         }
30209     });
30210     
30211     
30212 };
30213
30214 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
30215      
30216      
30217     editable : false,
30218      
30219     selectedClass: 'x-menu-item-checked', 
30220     
30221     // private
30222     onRender : function(ct, position){
30223         var _t = this;
30224         
30225         
30226         
30227         if(!this.tpl){
30228             var cls = 'x-combo-list';
30229
30230             
30231             this.tpl =  new Roo.Template({
30232                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
30233                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
30234                    '<span>{' + this.displayField + '}</span>' +
30235                     '</div>' 
30236                 
30237             });
30238         }
30239  
30240         
30241         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
30242         this.view.singleSelect = false;
30243         this.view.multiSelect = true;
30244         this.view.toggleSelect = true;
30245         this.pageTb.add(new Roo.Toolbar.Fill(), {
30246             
30247             text: 'Done',
30248             handler: function()
30249             {
30250                 _t.collapse();
30251             }
30252         });
30253     },
30254     
30255     onViewOver : function(e, t){
30256         // do nothing...
30257         return;
30258         
30259     },
30260     
30261     onViewClick : function(doFocus,index){
30262         return;
30263         
30264     },
30265     select: function () {
30266         //Roo.log("SELECT CALLED");
30267     },
30268      
30269     selectByValue : function(xv, scrollIntoView){
30270         var ar = this.getValueArray();
30271         var sels = [];
30272         
30273         Roo.each(ar, function(v) {
30274             if(v === undefined || v === null){
30275                 return;
30276             }
30277             var r = this.findRecord(this.valueField, v);
30278             if(r){
30279                 sels.push(this.store.indexOf(r))
30280                 
30281             }
30282         },this);
30283         this.view.select(sels);
30284         return false;
30285     },
30286     
30287     
30288     
30289     onSelect : function(record, index){
30290        // Roo.log("onselect Called");
30291        // this is only called by the clear button now..
30292         this.view.clearSelections();
30293         this.setValue('[]');
30294         if (this.value != this.valueBefore) {
30295             this.fireEvent('change', this, this.value, this.valueBefore);
30296         }
30297     },
30298     getValueArray : function()
30299     {
30300         var ar = [] ;
30301         
30302         try {
30303             //Roo.log(this.value);
30304             if (typeof(this.value) == 'undefined') {
30305                 return [];
30306             }
30307             var ar = Roo.decode(this.value);
30308             return  ar instanceof Array ? ar : []; //?? valid?
30309             
30310         } catch(e) {
30311             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
30312             return [];
30313         }
30314          
30315     },
30316     expand : function ()
30317     {
30318         Roo.form.ComboCheck.superclass.expand.call(this);
30319         this.valueBefore = this.value;
30320         
30321
30322     },
30323     
30324     collapse : function(){
30325         Roo.form.ComboCheck.superclass.collapse.call(this);
30326         var sl = this.view.getSelectedIndexes();
30327         var st = this.store;
30328         var nv = [];
30329         var tv = [];
30330         var r;
30331         Roo.each(sl, function(i) {
30332             r = st.getAt(i);
30333             nv.push(r.get(this.valueField));
30334         },this);
30335         this.setValue(Roo.encode(nv));
30336         if (this.value != this.valueBefore) {
30337
30338             this.fireEvent('change', this, this.value, this.valueBefore);
30339         }
30340         
30341     },
30342     
30343     setValue : function(v){
30344         // Roo.log(v);
30345         this.value = v;
30346         
30347         var vals = this.getValueArray();
30348         var tv = [];
30349         Roo.each(vals, function(k) {
30350             var r = this.findRecord(this.valueField, k);
30351             if(r){
30352                 tv.push(r.data[this.displayField]);
30353             }else if(this.valueNotFoundText !== undefined){
30354                 tv.push( this.valueNotFoundText );
30355             }
30356         },this);
30357        // Roo.log(tv);
30358         
30359         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
30360         this.hiddenField.value = v;
30361         this.value = v;
30362     }
30363     
30364 });//<script type="text/javasscript">
30365  
30366
30367 /**
30368  * @class Roo.DDView
30369  * A DnD enabled version of Roo.View.
30370  * @param {Element/String} container The Element in which to create the View.
30371  * @param {String} tpl The template string used to create the markup for each element of the View
30372  * @param {Object} config The configuration properties. These include all the config options of
30373  * {@link Roo.View} plus some specific to this class.<br>
30374  * <p>
30375  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
30376  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
30377  * <p>
30378  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
30379 .x-view-drag-insert-above {
30380         border-top:1px dotted #3366cc;
30381 }
30382 .x-view-drag-insert-below {
30383         border-bottom:1px dotted #3366cc;
30384 }
30385 </code></pre>
30386  * 
30387  */
30388  
30389 Roo.DDView = function(container, tpl, config) {
30390     Roo.DDView.superclass.constructor.apply(this, arguments);
30391     this.getEl().setStyle("outline", "0px none");
30392     this.getEl().unselectable();
30393     if (this.dragGroup) {
30394                 this.setDraggable(this.dragGroup.split(","));
30395     }
30396     if (this.dropGroup) {
30397                 this.setDroppable(this.dropGroup.split(","));
30398     }
30399     if (this.deletable) {
30400         this.setDeletable();
30401     }
30402     this.isDirtyFlag = false;
30403         this.addEvents({
30404                 "drop" : true
30405         });
30406 };
30407
30408 Roo.extend(Roo.DDView, Roo.View, {
30409 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
30410 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
30411 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
30412 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
30413
30414         isFormField: true,
30415
30416         reset: Roo.emptyFn,
30417         
30418         clearInvalid: Roo.form.Field.prototype.clearInvalid,
30419
30420         validate: function() {
30421                 return true;
30422         },
30423         
30424         destroy: function() {
30425                 this.purgeListeners();
30426                 this.getEl.removeAllListeners();
30427                 this.getEl().remove();
30428                 if (this.dragZone) {
30429                         if (this.dragZone.destroy) {
30430                                 this.dragZone.destroy();
30431                         }
30432                 }
30433                 if (this.dropZone) {
30434                         if (this.dropZone.destroy) {
30435                                 this.dropZone.destroy();
30436                         }
30437                 }
30438         },
30439
30440 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
30441         getName: function() {
30442                 return this.name;
30443         },
30444
30445 /**     Loads the View from a JSON string representing the Records to put into the Store. */
30446         setValue: function(v) {
30447                 if (!this.store) {
30448                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
30449                 }
30450                 var data = {};
30451                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
30452                 this.store.proxy = new Roo.data.MemoryProxy(data);
30453                 this.store.load();
30454         },
30455
30456 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
30457         getValue: function() {
30458                 var result = '(';
30459                 this.store.each(function(rec) {
30460                         result += rec.id + ',';
30461                 });
30462                 return result.substr(0, result.length - 1) + ')';
30463         },
30464         
30465         getIds: function() {
30466                 var i = 0, result = new Array(this.store.getCount());
30467                 this.store.each(function(rec) {
30468                         result[i++] = rec.id;
30469                 });
30470                 return result;
30471         },
30472         
30473         isDirty: function() {
30474                 return this.isDirtyFlag;
30475         },
30476
30477 /**
30478  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
30479  *      whole Element becomes the target, and this causes the drop gesture to append.
30480  */
30481     getTargetFromEvent : function(e) {
30482                 var target = e.getTarget();
30483                 while ((target !== null) && (target.parentNode != this.el.dom)) {
30484                 target = target.parentNode;
30485                 }
30486                 if (!target) {
30487                         target = this.el.dom.lastChild || this.el.dom;
30488                 }
30489                 return target;
30490     },
30491
30492 /**
30493  *      Create the drag data which consists of an object which has the property "ddel" as
30494  *      the drag proxy element. 
30495  */
30496     getDragData : function(e) {
30497         var target = this.findItemFromChild(e.getTarget());
30498                 if(target) {
30499                         this.handleSelection(e);
30500                         var selNodes = this.getSelectedNodes();
30501             var dragData = {
30502                 source: this,
30503                 copy: this.copy || (this.allowCopy && e.ctrlKey),
30504                 nodes: selNodes,
30505                 records: []
30506                         };
30507                         var selectedIndices = this.getSelectedIndexes();
30508                         for (var i = 0; i < selectedIndices.length; i++) {
30509                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
30510                         }
30511                         if (selNodes.length == 1) {
30512                                 dragData.ddel = target.cloneNode(true); // the div element
30513                         } else {
30514                                 var div = document.createElement('div'); // create the multi element drag "ghost"
30515                                 div.className = 'multi-proxy';
30516                                 for (var i = 0, len = selNodes.length; i < len; i++) {
30517                                         div.appendChild(selNodes[i].cloneNode(true));
30518                                 }
30519                                 dragData.ddel = div;
30520                         }
30521             //console.log(dragData)
30522             //console.log(dragData.ddel.innerHTML)
30523                         return dragData;
30524                 }
30525         //console.log('nodragData')
30526                 return false;
30527     },
30528     
30529 /**     Specify to which ddGroup items in this DDView may be dragged. */
30530     setDraggable: function(ddGroup) {
30531         if (ddGroup instanceof Array) {
30532                 Roo.each(ddGroup, this.setDraggable, this);
30533                 return;
30534         }
30535         if (this.dragZone) {
30536                 this.dragZone.addToGroup(ddGroup);
30537         } else {
30538                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
30539                                 containerScroll: true,
30540                                 ddGroup: ddGroup 
30541
30542                         });
30543 //                      Draggability implies selection. DragZone's mousedown selects the element.
30544                         if (!this.multiSelect) { this.singleSelect = true; }
30545
30546 //                      Wire the DragZone's handlers up to methods in *this*
30547                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
30548                 }
30549     },
30550
30551 /**     Specify from which ddGroup this DDView accepts drops. */
30552     setDroppable: function(ddGroup) {
30553         if (ddGroup instanceof Array) {
30554                 Roo.each(ddGroup, this.setDroppable, this);
30555                 return;
30556         }
30557         if (this.dropZone) {
30558                 this.dropZone.addToGroup(ddGroup);
30559         } else {
30560                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
30561                                 containerScroll: true,
30562                                 ddGroup: ddGroup
30563                         });
30564
30565 //                      Wire the DropZone's handlers up to methods in *this*
30566                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
30567                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
30568                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
30569                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
30570                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
30571                 }
30572     },
30573
30574 /**     Decide whether to drop above or below a View node. */
30575     getDropPoint : function(e, n, dd){
30576         if (n == this.el.dom) { return "above"; }
30577                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
30578                 var c = t + (b - t) / 2;
30579                 var y = Roo.lib.Event.getPageY(e);
30580                 if(y <= c) {
30581                         return "above";
30582                 }else{
30583                         return "below";
30584                 }
30585     },
30586
30587     onNodeEnter : function(n, dd, e, data){
30588                 return false;
30589     },
30590     
30591     onNodeOver : function(n, dd, e, data){
30592                 var pt = this.getDropPoint(e, n, dd);
30593                 // set the insert point style on the target node
30594                 var dragElClass = this.dropNotAllowed;
30595                 if (pt) {
30596                         var targetElClass;
30597                         if (pt == "above"){
30598                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
30599                                 targetElClass = "x-view-drag-insert-above";
30600                         } else {
30601                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
30602                                 targetElClass = "x-view-drag-insert-below";
30603                         }
30604                         if (this.lastInsertClass != targetElClass){
30605                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
30606                                 this.lastInsertClass = targetElClass;
30607                         }
30608                 }
30609                 return dragElClass;
30610         },
30611
30612     onNodeOut : function(n, dd, e, data){
30613                 this.removeDropIndicators(n);
30614     },
30615
30616     onNodeDrop : function(n, dd, e, data){
30617         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
30618                 return false;
30619         }
30620         var pt = this.getDropPoint(e, n, dd);
30621                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
30622                 if (pt == "below") { insertAt++; }
30623                 for (var i = 0; i < data.records.length; i++) {
30624                         var r = data.records[i];
30625                         var dup = this.store.getById(r.id);
30626                         if (dup && (dd != this.dragZone)) {
30627                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
30628                         } else {
30629                                 if (data.copy) {
30630                                         this.store.insert(insertAt++, r.copy());
30631                                 } else {
30632                                         data.source.isDirtyFlag = true;
30633                                         r.store.remove(r);
30634                                         this.store.insert(insertAt++, r);
30635                                 }
30636                                 this.isDirtyFlag = true;
30637                         }
30638                 }
30639                 this.dragZone.cachedTarget = null;
30640                 return true;
30641     },
30642
30643     removeDropIndicators : function(n){
30644                 if(n){
30645                         Roo.fly(n).removeClass([
30646                                 "x-view-drag-insert-above",
30647                                 "x-view-drag-insert-below"]);
30648                         this.lastInsertClass = "_noclass";
30649                 }
30650     },
30651
30652 /**
30653  *      Utility method. Add a delete option to the DDView's context menu.
30654  *      @param {String} imageUrl The URL of the "delete" icon image.
30655  */
30656         setDeletable: function(imageUrl) {
30657                 if (!this.singleSelect && !this.multiSelect) {
30658                         this.singleSelect = true;
30659                 }
30660                 var c = this.getContextMenu();
30661                 this.contextMenu.on("itemclick", function(item) {
30662                         switch (item.id) {
30663                                 case "delete":
30664                                         this.remove(this.getSelectedIndexes());
30665                                         break;
30666                         }
30667                 }, this);
30668                 this.contextMenu.add({
30669                         icon: imageUrl,
30670                         id: "delete",
30671                         text: 'Delete'
30672                 });
30673         },
30674         
30675 /**     Return the context menu for this DDView. */
30676         getContextMenu: function() {
30677                 if (!this.contextMenu) {
30678 //                      Create the View's context menu
30679                         this.contextMenu = new Roo.menu.Menu({
30680                                 id: this.id + "-contextmenu"
30681                         });
30682                         this.el.on("contextmenu", this.showContextMenu, this);
30683                 }
30684                 return this.contextMenu;
30685         },
30686         
30687         disableContextMenu: function() {
30688                 if (this.contextMenu) {
30689                         this.el.un("contextmenu", this.showContextMenu, this);
30690                 }
30691         },
30692
30693         showContextMenu: function(e, item) {
30694         item = this.findItemFromChild(e.getTarget());
30695                 if (item) {
30696                         e.stopEvent();
30697                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
30698                         this.contextMenu.showAt(e.getXY());
30699             }
30700     },
30701
30702 /**
30703  *      Remove {@link Roo.data.Record}s at the specified indices.
30704  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
30705  */
30706     remove: function(selectedIndices) {
30707                 selectedIndices = [].concat(selectedIndices);
30708                 for (var i = 0; i < selectedIndices.length; i++) {
30709                         var rec = this.store.getAt(selectedIndices[i]);
30710                         this.store.remove(rec);
30711                 }
30712     },
30713
30714 /**
30715  *      Double click fires the event, but also, if this is draggable, and there is only one other
30716  *      related DropZone, it transfers the selected node.
30717  */
30718     onDblClick : function(e){
30719         var item = this.findItemFromChild(e.getTarget());
30720         if(item){
30721             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
30722                 return false;
30723             }
30724             if (this.dragGroup) {
30725                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
30726                     while (targets.indexOf(this.dropZone) > -1) {
30727                             targets.remove(this.dropZone);
30728                                 }
30729                     if (targets.length == 1) {
30730                                         this.dragZone.cachedTarget = null;
30731                         var el = Roo.get(targets[0].getEl());
30732                         var box = el.getBox(true);
30733                         targets[0].onNodeDrop(el.dom, {
30734                                 target: el.dom,
30735                                 xy: [box.x, box.y + box.height - 1]
30736                         }, null, this.getDragData(e));
30737                     }
30738                 }
30739         }
30740     },
30741     
30742     handleSelection: function(e) {
30743                 this.dragZone.cachedTarget = null;
30744         var item = this.findItemFromChild(e.getTarget());
30745         if (!item) {
30746                 this.clearSelections(true);
30747                 return;
30748         }
30749                 if (item && (this.multiSelect || this.singleSelect)){
30750                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
30751                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
30752                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
30753                                 this.unselect(item);
30754                         } else {
30755                                 this.select(item, this.multiSelect && e.ctrlKey);
30756                                 this.lastSelection = item;
30757                         }
30758                 }
30759     },
30760
30761     onItemClick : function(item, index, e){
30762                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
30763                         return false;
30764                 }
30765                 return true;
30766     },
30767
30768     unselect : function(nodeInfo, suppressEvent){
30769                 var node = this.getNode(nodeInfo);
30770                 if(node && this.isSelected(node)){
30771                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
30772                                 Roo.fly(node).removeClass(this.selectedClass);
30773                                 this.selections.remove(node);
30774                                 if(!suppressEvent){
30775                                         this.fireEvent("selectionchange", this, this.selections);
30776                                 }
30777                         }
30778                 }
30779     }
30780 });
30781 /*
30782  * Based on:
30783  * Ext JS Library 1.1.1
30784  * Copyright(c) 2006-2007, Ext JS, LLC.
30785  *
30786  * Originally Released Under LGPL - original licence link has changed is not relivant.
30787  *
30788  * Fork - LGPL
30789  * <script type="text/javascript">
30790  */
30791  
30792 /**
30793  * @class Roo.LayoutManager
30794  * @extends Roo.util.Observable
30795  * Base class for layout managers.
30796  */
30797 Roo.LayoutManager = function(container, config){
30798     Roo.LayoutManager.superclass.constructor.call(this);
30799     this.el = Roo.get(container);
30800     // ie scrollbar fix
30801     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
30802         document.body.scroll = "no";
30803     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
30804         this.el.position('relative');
30805     }
30806     this.id = this.el.id;
30807     this.el.addClass("x-layout-container");
30808     /** false to disable window resize monitoring @type Boolean */
30809     this.monitorWindowResize = true;
30810     this.regions = {};
30811     this.addEvents({
30812         /**
30813          * @event layout
30814          * Fires when a layout is performed. 
30815          * @param {Roo.LayoutManager} this
30816          */
30817         "layout" : true,
30818         /**
30819          * @event regionresized
30820          * Fires when the user resizes a region. 
30821          * @param {Roo.LayoutRegion} region The resized region
30822          * @param {Number} newSize The new size (width for east/west, height for north/south)
30823          */
30824         "regionresized" : true,
30825         /**
30826          * @event regioncollapsed
30827          * Fires when a region is collapsed. 
30828          * @param {Roo.LayoutRegion} region The collapsed region
30829          */
30830         "regioncollapsed" : true,
30831         /**
30832          * @event regionexpanded
30833          * Fires when a region is expanded.  
30834          * @param {Roo.LayoutRegion} region The expanded region
30835          */
30836         "regionexpanded" : true
30837     });
30838     this.updating = false;
30839     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
30840 };
30841
30842 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
30843     /**
30844      * Returns true if this layout is currently being updated
30845      * @return {Boolean}
30846      */
30847     isUpdating : function(){
30848         return this.updating; 
30849     },
30850     
30851     /**
30852      * Suspend the LayoutManager from doing auto-layouts while
30853      * making multiple add or remove calls
30854      */
30855     beginUpdate : function(){
30856         this.updating = true;    
30857     },
30858     
30859     /**
30860      * Restore auto-layouts and optionally disable the manager from performing a layout
30861      * @param {Boolean} noLayout true to disable a layout update 
30862      */
30863     endUpdate : function(noLayout){
30864         this.updating = false;
30865         if(!noLayout){
30866             this.layout();
30867         }    
30868     },
30869     
30870     layout: function(){
30871         
30872     },
30873     
30874     onRegionResized : function(region, newSize){
30875         this.fireEvent("regionresized", region, newSize);
30876         this.layout();
30877     },
30878     
30879     onRegionCollapsed : function(region){
30880         this.fireEvent("regioncollapsed", region);
30881     },
30882     
30883     onRegionExpanded : function(region){
30884         this.fireEvent("regionexpanded", region);
30885     },
30886         
30887     /**
30888      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
30889      * performs box-model adjustments.
30890      * @return {Object} The size as an object {width: (the width), height: (the height)}
30891      */
30892     getViewSize : function(){
30893         var size;
30894         if(this.el.dom != document.body){
30895             size = this.el.getSize();
30896         }else{
30897             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
30898         }
30899         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
30900         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
30901         return size;
30902     },
30903     
30904     /**
30905      * Returns the Element this layout is bound to.
30906      * @return {Roo.Element}
30907      */
30908     getEl : function(){
30909         return this.el;
30910     },
30911     
30912     /**
30913      * Returns the specified region.
30914      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
30915      * @return {Roo.LayoutRegion}
30916      */
30917     getRegion : function(target){
30918         return this.regions[target.toLowerCase()];
30919     },
30920     
30921     onWindowResize : function(){
30922         if(this.monitorWindowResize){
30923             this.layout();
30924         }
30925     }
30926 });/*
30927  * Based on:
30928  * Ext JS Library 1.1.1
30929  * Copyright(c) 2006-2007, Ext JS, LLC.
30930  *
30931  * Originally Released Under LGPL - original licence link has changed is not relivant.
30932  *
30933  * Fork - LGPL
30934  * <script type="text/javascript">
30935  */
30936 /**
30937  * @class Roo.BorderLayout
30938  * @extends Roo.LayoutManager
30939  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
30940  * please see: <br><br>
30941  * <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>
30942  * <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>
30943  * Example:
30944  <pre><code>
30945  var layout = new Roo.BorderLayout(document.body, {
30946     north: {
30947         initialSize: 25,
30948         titlebar: false
30949     },
30950     west: {
30951         split:true,
30952         initialSize: 200,
30953         minSize: 175,
30954         maxSize: 400,
30955         titlebar: true,
30956         collapsible: true
30957     },
30958     east: {
30959         split:true,
30960         initialSize: 202,
30961         minSize: 175,
30962         maxSize: 400,
30963         titlebar: true,
30964         collapsible: true
30965     },
30966     south: {
30967         split:true,
30968         initialSize: 100,
30969         minSize: 100,
30970         maxSize: 200,
30971         titlebar: true,
30972         collapsible: true
30973     },
30974     center: {
30975         titlebar: true,
30976         autoScroll:true,
30977         resizeTabs: true,
30978         minTabWidth: 50,
30979         preferredTabWidth: 150
30980     }
30981 });
30982
30983 // shorthand
30984 var CP = Roo.ContentPanel;
30985
30986 layout.beginUpdate();
30987 layout.add("north", new CP("north", "North"));
30988 layout.add("south", new CP("south", {title: "South", closable: true}));
30989 layout.add("west", new CP("west", {title: "West"}));
30990 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
30991 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
30992 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
30993 layout.getRegion("center").showPanel("center1");
30994 layout.endUpdate();
30995 </code></pre>
30996
30997 <b>The container the layout is rendered into can be either the body element or any other element.
30998 If it is not the body element, the container needs to either be an absolute positioned element,
30999 or you will need to add "position:relative" to the css of the container.  You will also need to specify
31000 the container size if it is not the body element.</b>
31001
31002 * @constructor
31003 * Create a new BorderLayout
31004 * @param {String/HTMLElement/Element} container The container this layout is bound to
31005 * @param {Object} config Configuration options
31006  */
31007 Roo.BorderLayout = function(container, config){
31008     config = config || {};
31009     Roo.BorderLayout.superclass.constructor.call(this, container, config);
31010     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
31011     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
31012         var target = this.factory.validRegions[i];
31013         if(config[target]){
31014             this.addRegion(target, config[target]);
31015         }
31016     }
31017 };
31018
31019 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
31020     /**
31021      * Creates and adds a new region if it doesn't already exist.
31022      * @param {String} target The target region key (north, south, east, west or center).
31023      * @param {Object} config The regions config object
31024      * @return {BorderLayoutRegion} The new region
31025      */
31026     addRegion : function(target, config){
31027         if(!this.regions[target]){
31028             var r = this.factory.create(target, this, config);
31029             this.bindRegion(target, r);
31030         }
31031         return this.regions[target];
31032     },
31033
31034     // private (kinda)
31035     bindRegion : function(name, r){
31036         this.regions[name] = r;
31037         r.on("visibilitychange", this.layout, this);
31038         r.on("paneladded", this.layout, this);
31039         r.on("panelremoved", this.layout, this);
31040         r.on("invalidated", this.layout, this);
31041         r.on("resized", this.onRegionResized, this);
31042         r.on("collapsed", this.onRegionCollapsed, this);
31043         r.on("expanded", this.onRegionExpanded, this);
31044     },
31045
31046     /**
31047      * Performs a layout update.
31048      */
31049     layout : function(){
31050         if(this.updating) return;
31051         var size = this.getViewSize();
31052         var w = size.width;
31053         var h = size.height;
31054         var centerW = w;
31055         var centerH = h;
31056         var centerY = 0;
31057         var centerX = 0;
31058         //var x = 0, y = 0;
31059
31060         var rs = this.regions;
31061         var north = rs["north"];
31062         var south = rs["south"]; 
31063         var west = rs["west"];
31064         var east = rs["east"];
31065         var center = rs["center"];
31066         //if(this.hideOnLayout){ // not supported anymore
31067             //c.el.setStyle("display", "none");
31068         //}
31069         if(north && north.isVisible()){
31070             var b = north.getBox();
31071             var m = north.getMargins();
31072             b.width = w - (m.left+m.right);
31073             b.x = m.left;
31074             b.y = m.top;
31075             centerY = b.height + b.y + m.bottom;
31076             centerH -= centerY;
31077             north.updateBox(this.safeBox(b));
31078         }
31079         if(south && south.isVisible()){
31080             var b = south.getBox();
31081             var m = south.getMargins();
31082             b.width = w - (m.left+m.right);
31083             b.x = m.left;
31084             var totalHeight = (b.height + m.top + m.bottom);
31085             b.y = h - totalHeight + m.top;
31086             centerH -= totalHeight;
31087             south.updateBox(this.safeBox(b));
31088         }
31089         if(west && west.isVisible()){
31090             var b = west.getBox();
31091             var m = west.getMargins();
31092             b.height = centerH - (m.top+m.bottom);
31093             b.x = m.left;
31094             b.y = centerY + m.top;
31095             var totalWidth = (b.width + m.left + m.right);
31096             centerX += totalWidth;
31097             centerW -= totalWidth;
31098             west.updateBox(this.safeBox(b));
31099         }
31100         if(east && east.isVisible()){
31101             var b = east.getBox();
31102             var m = east.getMargins();
31103             b.height = centerH - (m.top+m.bottom);
31104             var totalWidth = (b.width + m.left + m.right);
31105             b.x = w - totalWidth + m.left;
31106             b.y = centerY + m.top;
31107             centerW -= totalWidth;
31108             east.updateBox(this.safeBox(b));
31109         }
31110         if(center){
31111             var m = center.getMargins();
31112             var centerBox = {
31113                 x: centerX + m.left,
31114                 y: centerY + m.top,
31115                 width: centerW - (m.left+m.right),
31116                 height: centerH - (m.top+m.bottom)
31117             };
31118             //if(this.hideOnLayout){
31119                 //center.el.setStyle("display", "block");
31120             //}
31121             center.updateBox(this.safeBox(centerBox));
31122         }
31123         this.el.repaint();
31124         this.fireEvent("layout", this);
31125     },
31126
31127     // private
31128     safeBox : function(box){
31129         box.width = Math.max(0, box.width);
31130         box.height = Math.max(0, box.height);
31131         return box;
31132     },
31133
31134     /**
31135      * Adds a ContentPanel (or subclass) to this layout.
31136      * @param {String} target The target region key (north, south, east, west or center).
31137      * @param {Roo.ContentPanel} panel The panel to add
31138      * @return {Roo.ContentPanel} The added panel
31139      */
31140     add : function(target, panel){
31141          
31142         target = target.toLowerCase();
31143         return this.regions[target].add(panel);
31144     },
31145
31146     /**
31147      * Remove a ContentPanel (or subclass) to this layout.
31148      * @param {String} target The target region key (north, south, east, west or center).
31149      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
31150      * @return {Roo.ContentPanel} The removed panel
31151      */
31152     remove : function(target, panel){
31153         target = target.toLowerCase();
31154         return this.regions[target].remove(panel);
31155     },
31156
31157     /**
31158      * Searches all regions for a panel with the specified id
31159      * @param {String} panelId
31160      * @return {Roo.ContentPanel} The panel or null if it wasn't found
31161      */
31162     findPanel : function(panelId){
31163         var rs = this.regions;
31164         for(var target in rs){
31165             if(typeof rs[target] != "function"){
31166                 var p = rs[target].getPanel(panelId);
31167                 if(p){
31168                     return p;
31169                 }
31170             }
31171         }
31172         return null;
31173     },
31174
31175     /**
31176      * Searches all regions for a panel with the specified id and activates (shows) it.
31177      * @param {String/ContentPanel} panelId The panels id or the panel itself
31178      * @return {Roo.ContentPanel} The shown panel or null
31179      */
31180     showPanel : function(panelId) {
31181       var rs = this.regions;
31182       for(var target in rs){
31183          var r = rs[target];
31184          if(typeof r != "function"){
31185             if(r.hasPanel(panelId)){
31186                return r.showPanel(panelId);
31187             }
31188          }
31189       }
31190       return null;
31191    },
31192
31193    /**
31194      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
31195      * @param {Roo.state.Provider} provider (optional) An alternate state provider
31196      */
31197     restoreState : function(provider){
31198         if(!provider){
31199             provider = Roo.state.Manager;
31200         }
31201         var sm = new Roo.LayoutStateManager();
31202         sm.init(this, provider);
31203     },
31204
31205     /**
31206      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
31207      * object should contain properties for each region to add ContentPanels to, and each property's value should be
31208      * a valid ContentPanel config object.  Example:
31209      * <pre><code>
31210 // Create the main layout
31211 var layout = new Roo.BorderLayout('main-ct', {
31212     west: {
31213         split:true,
31214         minSize: 175,
31215         titlebar: true
31216     },
31217     center: {
31218         title:'Components'
31219     }
31220 }, 'main-ct');
31221
31222 // Create and add multiple ContentPanels at once via configs
31223 layout.batchAdd({
31224    west: {
31225        id: 'source-files',
31226        autoCreate:true,
31227        title:'Ext Source Files',
31228        autoScroll:true,
31229        fitToFrame:true
31230    },
31231    center : {
31232        el: cview,
31233        autoScroll:true,
31234        fitToFrame:true,
31235        toolbar: tb,
31236        resizeEl:'cbody'
31237    }
31238 });
31239 </code></pre>
31240      * @param {Object} regions An object containing ContentPanel configs by region name
31241      */
31242     batchAdd : function(regions){
31243         this.beginUpdate();
31244         for(var rname in regions){
31245             var lr = this.regions[rname];
31246             if(lr){
31247                 this.addTypedPanels(lr, regions[rname]);
31248             }
31249         }
31250         this.endUpdate();
31251     },
31252
31253     // private
31254     addTypedPanels : function(lr, ps){
31255         if(typeof ps == 'string'){
31256             lr.add(new Roo.ContentPanel(ps));
31257         }
31258         else if(ps instanceof Array){
31259             for(var i =0, len = ps.length; i < len; i++){
31260                 this.addTypedPanels(lr, ps[i]);
31261             }
31262         }
31263         else if(!ps.events){ // raw config?
31264             var el = ps.el;
31265             delete ps.el; // prevent conflict
31266             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
31267         }
31268         else {  // panel object assumed!
31269             lr.add(ps);
31270         }
31271     },
31272     /**
31273      * Adds a xtype elements to the layout.
31274      * <pre><code>
31275
31276 layout.addxtype({
31277        xtype : 'ContentPanel',
31278        region: 'west',
31279        items: [ .... ]
31280    }
31281 );
31282
31283 layout.addxtype({
31284         xtype : 'NestedLayoutPanel',
31285         region: 'west',
31286         layout: {
31287            center: { },
31288            west: { }   
31289         },
31290         items : [ ... list of content panels or nested layout panels.. ]
31291    }
31292 );
31293 </code></pre>
31294      * @param {Object} cfg Xtype definition of item to add.
31295      */
31296     addxtype : function(cfg)
31297     {
31298         // basically accepts a pannel...
31299         // can accept a layout region..!?!?
31300         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
31301         
31302         if (!cfg.xtype.match(/Panel$/)) {
31303             return false;
31304         }
31305         var ret = false;
31306         
31307         if (typeof(cfg.region) == 'undefined') {
31308             Roo.log("Failed to add Panel, region was not set");
31309             Roo.log(cfg);
31310             return false;
31311         }
31312         var region = cfg.region;
31313         delete cfg.region;
31314         
31315           
31316         var xitems = [];
31317         if (cfg.items) {
31318             xitems = cfg.items;
31319             delete cfg.items;
31320         }
31321         var nb = false;
31322         
31323         switch(cfg.xtype) 
31324         {
31325             case 'ContentPanel':  // ContentPanel (el, cfg)
31326             case 'ScrollPanel':  // ContentPanel (el, cfg)
31327                 if(cfg.autoCreate) {
31328                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
31329                 } else {
31330                     var el = this.el.createChild();
31331                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
31332                 }
31333                 
31334                 this.add(region, ret);
31335                 break;
31336             
31337             
31338             case 'TreePanel': // our new panel!
31339                 cfg.el = this.el.createChild();
31340                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
31341                 this.add(region, ret);
31342                 break;
31343             
31344             case 'NestedLayoutPanel': 
31345                 // create a new Layout (which is  a Border Layout...
31346                 var el = this.el.createChild();
31347                 var clayout = cfg.layout;
31348                 delete cfg.layout;
31349                 clayout.items   = clayout.items  || [];
31350                 // replace this exitems with the clayout ones..
31351                 xitems = clayout.items;
31352                  
31353                 
31354                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
31355                     cfg.background = false;
31356                 }
31357                 var layout = new Roo.BorderLayout(el, clayout);
31358                 
31359                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
31360                 //console.log('adding nested layout panel '  + cfg.toSource());
31361                 this.add(region, ret);
31362                 nb = {}; /// find first...
31363                 break;
31364                 
31365             case 'GridPanel': 
31366             
31367                 // needs grid and region
31368                 
31369                 //var el = this.getRegion(region).el.createChild();
31370                 var el = this.el.createChild();
31371                 // create the grid first...
31372                 
31373                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
31374                 delete cfg.grid;
31375                 if (region == 'center' && this.active ) {
31376                     cfg.background = false;
31377                 }
31378                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
31379                 
31380                 this.add(region, ret);
31381                 if (cfg.background) {
31382                     ret.on('activate', function(gp) {
31383                         if (!gp.grid.rendered) {
31384                             gp.grid.render();
31385                         }
31386                     });
31387                 } else {
31388                     grid.render();
31389                 }
31390                 break;
31391            
31392                
31393                 
31394                 
31395             default: 
31396                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
31397                 return null;
31398              // GridPanel (grid, cfg)
31399             
31400         }
31401         this.beginUpdate();
31402         // add children..
31403         var region = '';
31404         var abn = {};
31405         Roo.each(xitems, function(i)  {
31406             region = nb && i.region ? i.region : false;
31407             
31408             var add = ret.addxtype(i);
31409            
31410             if (region) {
31411                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
31412                 if (!i.background) {
31413                     abn[region] = nb[region] ;
31414                 }
31415             }
31416             
31417         });
31418         this.endUpdate();
31419
31420         // make the last non-background panel active..
31421         //if (nb) { Roo.log(abn); }
31422         if (nb) {
31423             
31424             for(var r in abn) {
31425                 region = this.getRegion(r);
31426                 if (region) {
31427                     // tried using nb[r], but it does not work..
31428                      
31429                     region.showPanel(abn[r]);
31430                    
31431                 }
31432             }
31433         }
31434         return ret;
31435         
31436     }
31437 });
31438
31439 /**
31440  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
31441  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
31442  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
31443  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
31444  * <pre><code>
31445 // shorthand
31446 var CP = Roo.ContentPanel;
31447
31448 var layout = Roo.BorderLayout.create({
31449     north: {
31450         initialSize: 25,
31451         titlebar: false,
31452         panels: [new CP("north", "North")]
31453     },
31454     west: {
31455         split:true,
31456         initialSize: 200,
31457         minSize: 175,
31458         maxSize: 400,
31459         titlebar: true,
31460         collapsible: true,
31461         panels: [new CP("west", {title: "West"})]
31462     },
31463     east: {
31464         split:true,
31465         initialSize: 202,
31466         minSize: 175,
31467         maxSize: 400,
31468         titlebar: true,
31469         collapsible: true,
31470         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
31471     },
31472     south: {
31473         split:true,
31474         initialSize: 100,
31475         minSize: 100,
31476         maxSize: 200,
31477         titlebar: true,
31478         collapsible: true,
31479         panels: [new CP("south", {title: "South", closable: true})]
31480     },
31481     center: {
31482         titlebar: true,
31483         autoScroll:true,
31484         resizeTabs: true,
31485         minTabWidth: 50,
31486         preferredTabWidth: 150,
31487         panels: [
31488             new CP("center1", {title: "Close Me", closable: true}),
31489             new CP("center2", {title: "Center Panel", closable: false})
31490         ]
31491     }
31492 }, document.body);
31493
31494 layout.getRegion("center").showPanel("center1");
31495 </code></pre>
31496  * @param config
31497  * @param targetEl
31498  */
31499 Roo.BorderLayout.create = function(config, targetEl){
31500     var layout = new Roo.BorderLayout(targetEl || document.body, config);
31501     layout.beginUpdate();
31502     var regions = Roo.BorderLayout.RegionFactory.validRegions;
31503     for(var j = 0, jlen = regions.length; j < jlen; j++){
31504         var lr = regions[j];
31505         if(layout.regions[lr] && config[lr].panels){
31506             var r = layout.regions[lr];
31507             var ps = config[lr].panels;
31508             layout.addTypedPanels(r, ps);
31509         }
31510     }
31511     layout.endUpdate();
31512     return layout;
31513 };
31514
31515 // private
31516 Roo.BorderLayout.RegionFactory = {
31517     // private
31518     validRegions : ["north","south","east","west","center"],
31519
31520     // private
31521     create : function(target, mgr, config){
31522         target = target.toLowerCase();
31523         if(config.lightweight || config.basic){
31524             return new Roo.BasicLayoutRegion(mgr, config, target);
31525         }
31526         switch(target){
31527             case "north":
31528                 return new Roo.NorthLayoutRegion(mgr, config);
31529             case "south":
31530                 return new Roo.SouthLayoutRegion(mgr, config);
31531             case "east":
31532                 return new Roo.EastLayoutRegion(mgr, config);
31533             case "west":
31534                 return new Roo.WestLayoutRegion(mgr, config);
31535             case "center":
31536                 return new Roo.CenterLayoutRegion(mgr, config);
31537         }
31538         throw 'Layout region "'+target+'" not supported.';
31539     }
31540 };/*
31541  * Based on:
31542  * Ext JS Library 1.1.1
31543  * Copyright(c) 2006-2007, Ext JS, LLC.
31544  *
31545  * Originally Released Under LGPL - original licence link has changed is not relivant.
31546  *
31547  * Fork - LGPL
31548  * <script type="text/javascript">
31549  */
31550  
31551 /**
31552  * @class Roo.BasicLayoutRegion
31553  * @extends Roo.util.Observable
31554  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
31555  * and does not have a titlebar, tabs or any other features. All it does is size and position 
31556  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
31557  */
31558 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
31559     this.mgr = mgr;
31560     this.position  = pos;
31561     this.events = {
31562         /**
31563          * @scope Roo.BasicLayoutRegion
31564          */
31565         
31566         /**
31567          * @event beforeremove
31568          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
31569          * @param {Roo.LayoutRegion} this
31570          * @param {Roo.ContentPanel} panel The panel
31571          * @param {Object} e The cancel event object
31572          */
31573         "beforeremove" : true,
31574         /**
31575          * @event invalidated
31576          * Fires when the layout for this region is changed.
31577          * @param {Roo.LayoutRegion} this
31578          */
31579         "invalidated" : true,
31580         /**
31581          * @event visibilitychange
31582          * Fires when this region is shown or hidden 
31583          * @param {Roo.LayoutRegion} this
31584          * @param {Boolean} visibility true or false
31585          */
31586         "visibilitychange" : true,
31587         /**
31588          * @event paneladded
31589          * Fires when a panel is added. 
31590          * @param {Roo.LayoutRegion} this
31591          * @param {Roo.ContentPanel} panel The panel
31592          */
31593         "paneladded" : true,
31594         /**
31595          * @event panelremoved
31596          * Fires when a panel is removed. 
31597          * @param {Roo.LayoutRegion} this
31598          * @param {Roo.ContentPanel} panel The panel
31599          */
31600         "panelremoved" : true,
31601         /**
31602          * @event collapsed
31603          * Fires when this region is collapsed.
31604          * @param {Roo.LayoutRegion} this
31605          */
31606         "collapsed" : true,
31607         /**
31608          * @event expanded
31609          * Fires when this region is expanded.
31610          * @param {Roo.LayoutRegion} this
31611          */
31612         "expanded" : true,
31613         /**
31614          * @event slideshow
31615          * Fires when this region is slid into view.
31616          * @param {Roo.LayoutRegion} this
31617          */
31618         "slideshow" : true,
31619         /**
31620          * @event slidehide
31621          * Fires when this region slides out of view. 
31622          * @param {Roo.LayoutRegion} this
31623          */
31624         "slidehide" : true,
31625         /**
31626          * @event panelactivated
31627          * Fires when a panel is activated. 
31628          * @param {Roo.LayoutRegion} this
31629          * @param {Roo.ContentPanel} panel The activated panel
31630          */
31631         "panelactivated" : true,
31632         /**
31633          * @event resized
31634          * Fires when the user resizes this region. 
31635          * @param {Roo.LayoutRegion} this
31636          * @param {Number} newSize The new size (width for east/west, height for north/south)
31637          */
31638         "resized" : true
31639     };
31640     /** A collection of panels in this region. @type Roo.util.MixedCollection */
31641     this.panels = new Roo.util.MixedCollection();
31642     this.panels.getKey = this.getPanelId.createDelegate(this);
31643     this.box = null;
31644     this.activePanel = null;
31645     // ensure listeners are added...
31646     
31647     if (config.listeners || config.events) {
31648         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
31649             listeners : config.listeners || {},
31650             events : config.events || {}
31651         });
31652     }
31653     
31654     if(skipConfig !== true){
31655         this.applyConfig(config);
31656     }
31657 };
31658
31659 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
31660     getPanelId : function(p){
31661         return p.getId();
31662     },
31663     
31664     applyConfig : function(config){
31665         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
31666         this.config = config;
31667         
31668     },
31669     
31670     /**
31671      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
31672      * the width, for horizontal (north, south) the height.
31673      * @param {Number} newSize The new width or height
31674      */
31675     resizeTo : function(newSize){
31676         var el = this.el ? this.el :
31677                  (this.activePanel ? this.activePanel.getEl() : null);
31678         if(el){
31679             switch(this.position){
31680                 case "east":
31681                 case "west":
31682                     el.setWidth(newSize);
31683                     this.fireEvent("resized", this, newSize);
31684                 break;
31685                 case "north":
31686                 case "south":
31687                     el.setHeight(newSize);
31688                     this.fireEvent("resized", this, newSize);
31689                 break;                
31690             }
31691         }
31692     },
31693     
31694     getBox : function(){
31695         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
31696     },
31697     
31698     getMargins : function(){
31699         return this.margins;
31700     },
31701     
31702     updateBox : function(box){
31703         this.box = box;
31704         var el = this.activePanel.getEl();
31705         el.dom.style.left = box.x + "px";
31706         el.dom.style.top = box.y + "px";
31707         this.activePanel.setSize(box.width, box.height);
31708     },
31709     
31710     /**
31711      * Returns the container element for this region.
31712      * @return {Roo.Element}
31713      */
31714     getEl : function(){
31715         return this.activePanel;
31716     },
31717     
31718     /**
31719      * Returns true if this region is currently visible.
31720      * @return {Boolean}
31721      */
31722     isVisible : function(){
31723         return this.activePanel ? true : false;
31724     },
31725     
31726     setActivePanel : function(panel){
31727         panel = this.getPanel(panel);
31728         if(this.activePanel && this.activePanel != panel){
31729             this.activePanel.setActiveState(false);
31730             this.activePanel.getEl().setLeftTop(-10000,-10000);
31731         }
31732         this.activePanel = panel;
31733         panel.setActiveState(true);
31734         if(this.box){
31735             panel.setSize(this.box.width, this.box.height);
31736         }
31737         this.fireEvent("panelactivated", this, panel);
31738         this.fireEvent("invalidated");
31739     },
31740     
31741     /**
31742      * Show the specified panel.
31743      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
31744      * @return {Roo.ContentPanel} The shown panel or null
31745      */
31746     showPanel : function(panel){
31747         if(panel = this.getPanel(panel)){
31748             this.setActivePanel(panel);
31749         }
31750         return panel;
31751     },
31752     
31753     /**
31754      * Get the active panel for this region.
31755      * @return {Roo.ContentPanel} The active panel or null
31756      */
31757     getActivePanel : function(){
31758         return this.activePanel;
31759     },
31760     
31761     /**
31762      * Add the passed ContentPanel(s)
31763      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
31764      * @return {Roo.ContentPanel} The panel added (if only one was added)
31765      */
31766     add : function(panel){
31767         if(arguments.length > 1){
31768             for(var i = 0, len = arguments.length; i < len; i++) {
31769                 this.add(arguments[i]);
31770             }
31771             return null;
31772         }
31773         if(this.hasPanel(panel)){
31774             this.showPanel(panel);
31775             return panel;
31776         }
31777         var el = panel.getEl();
31778         if(el.dom.parentNode != this.mgr.el.dom){
31779             this.mgr.el.dom.appendChild(el.dom);
31780         }
31781         if(panel.setRegion){
31782             panel.setRegion(this);
31783         }
31784         this.panels.add(panel);
31785         el.setStyle("position", "absolute");
31786         if(!panel.background){
31787             this.setActivePanel(panel);
31788             if(this.config.initialSize && this.panels.getCount()==1){
31789                 this.resizeTo(this.config.initialSize);
31790             }
31791         }
31792         this.fireEvent("paneladded", this, panel);
31793         return panel;
31794     },
31795     
31796     /**
31797      * Returns true if the panel is in this region.
31798      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
31799      * @return {Boolean}
31800      */
31801     hasPanel : function(panel){
31802         if(typeof panel == "object"){ // must be panel obj
31803             panel = panel.getId();
31804         }
31805         return this.getPanel(panel) ? true : false;
31806     },
31807     
31808     /**
31809      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
31810      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
31811      * @param {Boolean} preservePanel Overrides the config preservePanel option
31812      * @return {Roo.ContentPanel} The panel that was removed
31813      */
31814     remove : function(panel, preservePanel){
31815         panel = this.getPanel(panel);
31816         if(!panel){
31817             return null;
31818         }
31819         var e = {};
31820         this.fireEvent("beforeremove", this, panel, e);
31821         if(e.cancel === true){
31822             return null;
31823         }
31824         var panelId = panel.getId();
31825         this.panels.removeKey(panelId);
31826         return panel;
31827     },
31828     
31829     /**
31830      * Returns the panel specified or null if it's not in this region.
31831      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
31832      * @return {Roo.ContentPanel}
31833      */
31834     getPanel : function(id){
31835         if(typeof id == "object"){ // must be panel obj
31836             return id;
31837         }
31838         return this.panels.get(id);
31839     },
31840     
31841     /**
31842      * Returns this regions position (north/south/east/west/center).
31843      * @return {String} 
31844      */
31845     getPosition: function(){
31846         return this.position;    
31847     }
31848 });/*
31849  * Based on:
31850  * Ext JS Library 1.1.1
31851  * Copyright(c) 2006-2007, Ext JS, LLC.
31852  *
31853  * Originally Released Under LGPL - original licence link has changed is not relivant.
31854  *
31855  * Fork - LGPL
31856  * <script type="text/javascript">
31857  */
31858  
31859 /**
31860  * @class Roo.LayoutRegion
31861  * @extends Roo.BasicLayoutRegion
31862  * This class represents a region in a layout manager.
31863  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
31864  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
31865  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
31866  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
31867  * @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})
31868  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
31869  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
31870  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
31871  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
31872  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
31873  * @cfg {String}    title           The title for the region (overrides panel titles)
31874  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
31875  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
31876  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
31877  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
31878  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
31879  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
31880  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
31881  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
31882  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
31883  * @cfg {Boolean}   showPin         True to show a pin button
31884  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
31885  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
31886  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
31887  * @cfg {Number}    width           For East/West panels
31888  * @cfg {Number}    height          For North/South panels
31889  * @cfg {Boolean}   split           To show the splitter
31890  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
31891  */
31892 Roo.LayoutRegion = function(mgr, config, pos){
31893     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
31894     var dh = Roo.DomHelper;
31895     /** This region's container element 
31896     * @type Roo.Element */
31897     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
31898     /** This region's title element 
31899     * @type Roo.Element */
31900
31901     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
31902         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
31903         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
31904     ]}, true);
31905     this.titleEl.enableDisplayMode();
31906     /** This region's title text element 
31907     * @type HTMLElement */
31908     this.titleTextEl = this.titleEl.dom.firstChild;
31909     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
31910     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
31911     this.closeBtn.enableDisplayMode();
31912     this.closeBtn.on("click", this.closeClicked, this);
31913     this.closeBtn.hide();
31914
31915     this.createBody(config);
31916     this.visible = true;
31917     this.collapsed = false;
31918
31919     if(config.hideWhenEmpty){
31920         this.hide();
31921         this.on("paneladded", this.validateVisibility, this);
31922         this.on("panelremoved", this.validateVisibility, this);
31923     }
31924     this.applyConfig(config);
31925 };
31926
31927 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
31928
31929     createBody : function(){
31930         /** This region's body element 
31931         * @type Roo.Element */
31932         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
31933     },
31934
31935     applyConfig : function(c){
31936         if(c.collapsible && this.position != "center" && !this.collapsedEl){
31937             var dh = Roo.DomHelper;
31938             if(c.titlebar !== false){
31939                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
31940                 this.collapseBtn.on("click", this.collapse, this);
31941                 this.collapseBtn.enableDisplayMode();
31942
31943                 if(c.showPin === true || this.showPin){
31944                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
31945                     this.stickBtn.enableDisplayMode();
31946                     this.stickBtn.on("click", this.expand, this);
31947                     this.stickBtn.hide();
31948                 }
31949             }
31950             /** This region's collapsed element
31951             * @type Roo.Element */
31952             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
31953                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
31954             ]}, true);
31955             if(c.floatable !== false){
31956                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
31957                this.collapsedEl.on("click", this.collapseClick, this);
31958             }
31959
31960             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
31961                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
31962                    id: "message", unselectable: "on", style:{"float":"left"}});
31963                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
31964              }
31965             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
31966             this.expandBtn.on("click", this.expand, this);
31967         }
31968         if(this.collapseBtn){
31969             this.collapseBtn.setVisible(c.collapsible == true);
31970         }
31971         this.cmargins = c.cmargins || this.cmargins ||
31972                          (this.position == "west" || this.position == "east" ?
31973                              {top: 0, left: 2, right:2, bottom: 0} :
31974                              {top: 2, left: 0, right:0, bottom: 2});
31975         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
31976         this.bottomTabs = c.tabPosition != "top";
31977         this.autoScroll = c.autoScroll || false;
31978         if(this.autoScroll){
31979             this.bodyEl.setStyle("overflow", "auto");
31980         }else{
31981             this.bodyEl.setStyle("overflow", "hidden");
31982         }
31983         //if(c.titlebar !== false){
31984             if((!c.titlebar && !c.title) || c.titlebar === false){
31985                 this.titleEl.hide();
31986             }else{
31987                 this.titleEl.show();
31988                 if(c.title){
31989                     this.titleTextEl.innerHTML = c.title;
31990                 }
31991             }
31992         //}
31993         this.duration = c.duration || .30;
31994         this.slideDuration = c.slideDuration || .45;
31995         this.config = c;
31996         if(c.collapsed){
31997             this.collapse(true);
31998         }
31999         if(c.hidden){
32000             this.hide();
32001         }
32002     },
32003     /**
32004      * Returns true if this region is currently visible.
32005      * @return {Boolean}
32006      */
32007     isVisible : function(){
32008         return this.visible;
32009     },
32010
32011     /**
32012      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
32013      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
32014      */
32015     setCollapsedTitle : function(title){
32016         title = title || "&#160;";
32017         if(this.collapsedTitleTextEl){
32018             this.collapsedTitleTextEl.innerHTML = title;
32019         }
32020     },
32021
32022     getBox : function(){
32023         var b;
32024         if(!this.collapsed){
32025             b = this.el.getBox(false, true);
32026         }else{
32027             b = this.collapsedEl.getBox(false, true);
32028         }
32029         return b;
32030     },
32031
32032     getMargins : function(){
32033         return this.collapsed ? this.cmargins : this.margins;
32034     },
32035
32036     highlight : function(){
32037         this.el.addClass("x-layout-panel-dragover");
32038     },
32039
32040     unhighlight : function(){
32041         this.el.removeClass("x-layout-panel-dragover");
32042     },
32043
32044     updateBox : function(box){
32045         this.box = box;
32046         if(!this.collapsed){
32047             this.el.dom.style.left = box.x + "px";
32048             this.el.dom.style.top = box.y + "px";
32049             this.updateBody(box.width, box.height);
32050         }else{
32051             this.collapsedEl.dom.style.left = box.x + "px";
32052             this.collapsedEl.dom.style.top = box.y + "px";
32053             this.collapsedEl.setSize(box.width, box.height);
32054         }
32055         if(this.tabs){
32056             this.tabs.autoSizeTabs();
32057         }
32058     },
32059
32060     updateBody : function(w, h){
32061         if(w !== null){
32062             this.el.setWidth(w);
32063             w -= this.el.getBorderWidth("rl");
32064             if(this.config.adjustments){
32065                 w += this.config.adjustments[0];
32066             }
32067         }
32068         if(h !== null){
32069             this.el.setHeight(h);
32070             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
32071             h -= this.el.getBorderWidth("tb");
32072             if(this.config.adjustments){
32073                 h += this.config.adjustments[1];
32074             }
32075             this.bodyEl.setHeight(h);
32076             if(this.tabs){
32077                 h = this.tabs.syncHeight(h);
32078             }
32079         }
32080         if(this.panelSize){
32081             w = w !== null ? w : this.panelSize.width;
32082             h = h !== null ? h : this.panelSize.height;
32083         }
32084         if(this.activePanel){
32085             var el = this.activePanel.getEl();
32086             w = w !== null ? w : el.getWidth();
32087             h = h !== null ? h : el.getHeight();
32088             this.panelSize = {width: w, height: h};
32089             this.activePanel.setSize(w, h);
32090         }
32091         if(Roo.isIE && this.tabs){
32092             this.tabs.el.repaint();
32093         }
32094     },
32095
32096     /**
32097      * Returns the container element for this region.
32098      * @return {Roo.Element}
32099      */
32100     getEl : function(){
32101         return this.el;
32102     },
32103
32104     /**
32105      * Hides this region.
32106      */
32107     hide : function(){
32108         if(!this.collapsed){
32109             this.el.dom.style.left = "-2000px";
32110             this.el.hide();
32111         }else{
32112             this.collapsedEl.dom.style.left = "-2000px";
32113             this.collapsedEl.hide();
32114         }
32115         this.visible = false;
32116         this.fireEvent("visibilitychange", this, false);
32117     },
32118
32119     /**
32120      * Shows this region if it was previously hidden.
32121      */
32122     show : function(){
32123         if(!this.collapsed){
32124             this.el.show();
32125         }else{
32126             this.collapsedEl.show();
32127         }
32128         this.visible = true;
32129         this.fireEvent("visibilitychange", this, true);
32130     },
32131
32132     closeClicked : function(){
32133         if(this.activePanel){
32134             this.remove(this.activePanel);
32135         }
32136     },
32137
32138     collapseClick : function(e){
32139         if(this.isSlid){
32140            e.stopPropagation();
32141            this.slideIn();
32142         }else{
32143            e.stopPropagation();
32144            this.slideOut();
32145         }
32146     },
32147
32148     /**
32149      * Collapses this region.
32150      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
32151      */
32152     collapse : function(skipAnim){
32153         if(this.collapsed) return;
32154         this.collapsed = true;
32155         if(this.split){
32156             this.split.el.hide();
32157         }
32158         if(this.config.animate && skipAnim !== true){
32159             this.fireEvent("invalidated", this);
32160             this.animateCollapse();
32161         }else{
32162             this.el.setLocation(-20000,-20000);
32163             this.el.hide();
32164             this.collapsedEl.show();
32165             this.fireEvent("collapsed", this);
32166             this.fireEvent("invalidated", this);
32167         }
32168     },
32169
32170     animateCollapse : function(){
32171         // overridden
32172     },
32173
32174     /**
32175      * Expands this region if it was previously collapsed.
32176      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
32177      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
32178      */
32179     expand : function(e, skipAnim){
32180         if(e) e.stopPropagation();
32181         if(!this.collapsed || this.el.hasActiveFx()) return;
32182         if(this.isSlid){
32183             this.afterSlideIn();
32184             skipAnim = true;
32185         }
32186         this.collapsed = false;
32187         if(this.config.animate && skipAnim !== true){
32188             this.animateExpand();
32189         }else{
32190             this.el.show();
32191             if(this.split){
32192                 this.split.el.show();
32193             }
32194             this.collapsedEl.setLocation(-2000,-2000);
32195             this.collapsedEl.hide();
32196             this.fireEvent("invalidated", this);
32197             this.fireEvent("expanded", this);
32198         }
32199     },
32200
32201     animateExpand : function(){
32202         // overridden
32203     },
32204
32205     initTabs : function()
32206     {
32207         this.bodyEl.setStyle("overflow", "hidden");
32208         var ts = new Roo.TabPanel(
32209                 this.bodyEl.dom,
32210                 {
32211                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
32212                     disableTooltips: this.config.disableTabTips,
32213                     toolbar : this.config.toolbar
32214                 }
32215         );
32216         if(this.config.hideTabs){
32217             ts.stripWrap.setDisplayed(false);
32218         }
32219         this.tabs = ts;
32220         ts.resizeTabs = this.config.resizeTabs === true;
32221         ts.minTabWidth = this.config.minTabWidth || 40;
32222         ts.maxTabWidth = this.config.maxTabWidth || 250;
32223         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
32224         ts.monitorResize = false;
32225         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
32226         ts.bodyEl.addClass('x-layout-tabs-body');
32227         this.panels.each(this.initPanelAsTab, this);
32228     },
32229
32230     initPanelAsTab : function(panel){
32231         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
32232                     this.config.closeOnTab && panel.isClosable());
32233         if(panel.tabTip !== undefined){
32234             ti.setTooltip(panel.tabTip);
32235         }
32236         ti.on("activate", function(){
32237               this.setActivePanel(panel);
32238         }, this);
32239         if(this.config.closeOnTab){
32240             ti.on("beforeclose", function(t, e){
32241                 e.cancel = true;
32242                 this.remove(panel);
32243             }, this);
32244         }
32245         return ti;
32246     },
32247
32248     updatePanelTitle : function(panel, title){
32249         if(this.activePanel == panel){
32250             this.updateTitle(title);
32251         }
32252         if(this.tabs){
32253             var ti = this.tabs.getTab(panel.getEl().id);
32254             ti.setText(title);
32255             if(panel.tabTip !== undefined){
32256                 ti.setTooltip(panel.tabTip);
32257             }
32258         }
32259     },
32260
32261     updateTitle : function(title){
32262         if(this.titleTextEl && !this.config.title){
32263             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
32264         }
32265     },
32266
32267     setActivePanel : function(panel){
32268         panel = this.getPanel(panel);
32269         if(this.activePanel && this.activePanel != panel){
32270             this.activePanel.setActiveState(false);
32271         }
32272         this.activePanel = panel;
32273         panel.setActiveState(true);
32274         if(this.panelSize){
32275             panel.setSize(this.panelSize.width, this.panelSize.height);
32276         }
32277         if(this.closeBtn){
32278             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
32279         }
32280         this.updateTitle(panel.getTitle());
32281         if(this.tabs){
32282             this.fireEvent("invalidated", this);
32283         }
32284         this.fireEvent("panelactivated", this, panel);
32285     },
32286
32287     /**
32288      * Shows the specified panel.
32289      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
32290      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
32291      */
32292     showPanel : function(panel){
32293         if(panel = this.getPanel(panel)){
32294             if(this.tabs){
32295                 var tab = this.tabs.getTab(panel.getEl().id);
32296                 if(tab.isHidden()){
32297                     this.tabs.unhideTab(tab.id);
32298                 }
32299                 tab.activate();
32300             }else{
32301                 this.setActivePanel(panel);
32302             }
32303         }
32304         return panel;
32305     },
32306
32307     /**
32308      * Get the active panel for this region.
32309      * @return {Roo.ContentPanel} The active panel or null
32310      */
32311     getActivePanel : function(){
32312         return this.activePanel;
32313     },
32314
32315     validateVisibility : function(){
32316         if(this.panels.getCount() < 1){
32317             this.updateTitle("&#160;");
32318             this.closeBtn.hide();
32319             this.hide();
32320         }else{
32321             if(!this.isVisible()){
32322                 this.show();
32323             }
32324         }
32325     },
32326
32327     /**
32328      * Adds the passed ContentPanel(s) to this region.
32329      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32330      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
32331      */
32332     add : function(panel){
32333         if(arguments.length > 1){
32334             for(var i = 0, len = arguments.length; i < len; i++) {
32335                 this.add(arguments[i]);
32336             }
32337             return null;
32338         }
32339         if(this.hasPanel(panel)){
32340             this.showPanel(panel);
32341             return panel;
32342         }
32343         panel.setRegion(this);
32344         this.panels.add(panel);
32345         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
32346             this.bodyEl.dom.appendChild(panel.getEl().dom);
32347             if(panel.background !== true){
32348                 this.setActivePanel(panel);
32349             }
32350             this.fireEvent("paneladded", this, panel);
32351             return panel;
32352         }
32353         if(!this.tabs){
32354             this.initTabs();
32355         }else{
32356             this.initPanelAsTab(panel);
32357         }
32358         if(panel.background !== true){
32359             this.tabs.activate(panel.getEl().id);
32360         }
32361         this.fireEvent("paneladded", this, panel);
32362         return panel;
32363     },
32364
32365     /**
32366      * Hides the tab for the specified panel.
32367      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32368      */
32369     hidePanel : function(panel){
32370         if(this.tabs && (panel = this.getPanel(panel))){
32371             this.tabs.hideTab(panel.getEl().id);
32372         }
32373     },
32374
32375     /**
32376      * Unhides the tab for a previously hidden panel.
32377      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32378      */
32379     unhidePanel : function(panel){
32380         if(this.tabs && (panel = this.getPanel(panel))){
32381             this.tabs.unhideTab(panel.getEl().id);
32382         }
32383     },
32384
32385     clearPanels : function(){
32386         while(this.panels.getCount() > 0){
32387              this.remove(this.panels.first());
32388         }
32389     },
32390
32391     /**
32392      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
32393      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32394      * @param {Boolean} preservePanel Overrides the config preservePanel option
32395      * @return {Roo.ContentPanel} The panel that was removed
32396      */
32397     remove : function(panel, preservePanel){
32398         panel = this.getPanel(panel);
32399         if(!panel){
32400             return null;
32401         }
32402         var e = {};
32403         this.fireEvent("beforeremove", this, panel, e);
32404         if(e.cancel === true){
32405             return null;
32406         }
32407         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
32408         var panelId = panel.getId();
32409         this.panels.removeKey(panelId);
32410         if(preservePanel){
32411             document.body.appendChild(panel.getEl().dom);
32412         }
32413         if(this.tabs){
32414             this.tabs.removeTab(panel.getEl().id);
32415         }else if (!preservePanel){
32416             this.bodyEl.dom.removeChild(panel.getEl().dom);
32417         }
32418         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
32419             var p = this.panels.first();
32420             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
32421             tempEl.appendChild(p.getEl().dom);
32422             this.bodyEl.update("");
32423             this.bodyEl.dom.appendChild(p.getEl().dom);
32424             tempEl = null;
32425             this.updateTitle(p.getTitle());
32426             this.tabs = null;
32427             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
32428             this.setActivePanel(p);
32429         }
32430         panel.setRegion(null);
32431         if(this.activePanel == panel){
32432             this.activePanel = null;
32433         }
32434         if(this.config.autoDestroy !== false && preservePanel !== true){
32435             try{panel.destroy();}catch(e){}
32436         }
32437         this.fireEvent("panelremoved", this, panel);
32438         return panel;
32439     },
32440
32441     /**
32442      * Returns the TabPanel component used by this region
32443      * @return {Roo.TabPanel}
32444      */
32445     getTabs : function(){
32446         return this.tabs;
32447     },
32448
32449     createTool : function(parentEl, className){
32450         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
32451             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
32452         btn.addClassOnOver("x-layout-tools-button-over");
32453         return btn;
32454     }
32455 });/*
32456  * Based on:
32457  * Ext JS Library 1.1.1
32458  * Copyright(c) 2006-2007, Ext JS, LLC.
32459  *
32460  * Originally Released Under LGPL - original licence link has changed is not relivant.
32461  *
32462  * Fork - LGPL
32463  * <script type="text/javascript">
32464  */
32465  
32466
32467
32468 /**
32469  * @class Roo.SplitLayoutRegion
32470  * @extends Roo.LayoutRegion
32471  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
32472  */
32473 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
32474     this.cursor = cursor;
32475     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
32476 };
32477
32478 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
32479     splitTip : "Drag to resize.",
32480     collapsibleSplitTip : "Drag to resize. Double click to hide.",
32481     useSplitTips : false,
32482
32483     applyConfig : function(config){
32484         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
32485         if(config.split){
32486             if(!this.split){
32487                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
32488                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
32489                 /** The SplitBar for this region 
32490                 * @type Roo.SplitBar */
32491                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
32492                 this.split.on("moved", this.onSplitMove, this);
32493                 this.split.useShim = config.useShim === true;
32494                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
32495                 if(this.useSplitTips){
32496                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
32497                 }
32498                 if(config.collapsible){
32499                     this.split.el.on("dblclick", this.collapse,  this);
32500                 }
32501             }
32502             if(typeof config.minSize != "undefined"){
32503                 this.split.minSize = config.minSize;
32504             }
32505             if(typeof config.maxSize != "undefined"){
32506                 this.split.maxSize = config.maxSize;
32507             }
32508             if(config.hideWhenEmpty || config.hidden || config.collapsed){
32509                 this.hideSplitter();
32510             }
32511         }
32512     },
32513
32514     getHMaxSize : function(){
32515          var cmax = this.config.maxSize || 10000;
32516          var center = this.mgr.getRegion("center");
32517          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
32518     },
32519
32520     getVMaxSize : function(){
32521          var cmax = this.config.maxSize || 10000;
32522          var center = this.mgr.getRegion("center");
32523          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
32524     },
32525
32526     onSplitMove : function(split, newSize){
32527         this.fireEvent("resized", this, newSize);
32528     },
32529     
32530     /** 
32531      * Returns the {@link Roo.SplitBar} for this region.
32532      * @return {Roo.SplitBar}
32533      */
32534     getSplitBar : function(){
32535         return this.split;
32536     },
32537     
32538     hide : function(){
32539         this.hideSplitter();
32540         Roo.SplitLayoutRegion.superclass.hide.call(this);
32541     },
32542
32543     hideSplitter : function(){
32544         if(this.split){
32545             this.split.el.setLocation(-2000,-2000);
32546             this.split.el.hide();
32547         }
32548     },
32549
32550     show : function(){
32551         if(this.split){
32552             this.split.el.show();
32553         }
32554         Roo.SplitLayoutRegion.superclass.show.call(this);
32555     },
32556     
32557     beforeSlide: function(){
32558         if(Roo.isGecko){// firefox overflow auto bug workaround
32559             this.bodyEl.clip();
32560             if(this.tabs) this.tabs.bodyEl.clip();
32561             if(this.activePanel){
32562                 this.activePanel.getEl().clip();
32563                 
32564                 if(this.activePanel.beforeSlide){
32565                     this.activePanel.beforeSlide();
32566                 }
32567             }
32568         }
32569     },
32570     
32571     afterSlide : function(){
32572         if(Roo.isGecko){// firefox overflow auto bug workaround
32573             this.bodyEl.unclip();
32574             if(this.tabs) this.tabs.bodyEl.unclip();
32575             if(this.activePanel){
32576                 this.activePanel.getEl().unclip();
32577                 if(this.activePanel.afterSlide){
32578                     this.activePanel.afterSlide();
32579                 }
32580             }
32581         }
32582     },
32583
32584     initAutoHide : function(){
32585         if(this.autoHide !== false){
32586             if(!this.autoHideHd){
32587                 var st = new Roo.util.DelayedTask(this.slideIn, this);
32588                 this.autoHideHd = {
32589                     "mouseout": function(e){
32590                         if(!e.within(this.el, true)){
32591                             st.delay(500);
32592                         }
32593                     },
32594                     "mouseover" : function(e){
32595                         st.cancel();
32596                     },
32597                     scope : this
32598                 };
32599             }
32600             this.el.on(this.autoHideHd);
32601         }
32602     },
32603
32604     clearAutoHide : function(){
32605         if(this.autoHide !== false){
32606             this.el.un("mouseout", this.autoHideHd.mouseout);
32607             this.el.un("mouseover", this.autoHideHd.mouseover);
32608         }
32609     },
32610
32611     clearMonitor : function(){
32612         Roo.get(document).un("click", this.slideInIf, this);
32613     },
32614
32615     // these names are backwards but not changed for compat
32616     slideOut : function(){
32617         if(this.isSlid || this.el.hasActiveFx()){
32618             return;
32619         }
32620         this.isSlid = true;
32621         if(this.collapseBtn){
32622             this.collapseBtn.hide();
32623         }
32624         this.closeBtnState = this.closeBtn.getStyle('display');
32625         this.closeBtn.hide();
32626         if(this.stickBtn){
32627             this.stickBtn.show();
32628         }
32629         this.el.show();
32630         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
32631         this.beforeSlide();
32632         this.el.setStyle("z-index", 10001);
32633         this.el.slideIn(this.getSlideAnchor(), {
32634             callback: function(){
32635                 this.afterSlide();
32636                 this.initAutoHide();
32637                 Roo.get(document).on("click", this.slideInIf, this);
32638                 this.fireEvent("slideshow", this);
32639             },
32640             scope: this,
32641             block: true
32642         });
32643     },
32644
32645     afterSlideIn : function(){
32646         this.clearAutoHide();
32647         this.isSlid = false;
32648         this.clearMonitor();
32649         this.el.setStyle("z-index", "");
32650         if(this.collapseBtn){
32651             this.collapseBtn.show();
32652         }
32653         this.closeBtn.setStyle('display', this.closeBtnState);
32654         if(this.stickBtn){
32655             this.stickBtn.hide();
32656         }
32657         this.fireEvent("slidehide", this);
32658     },
32659
32660     slideIn : function(cb){
32661         if(!this.isSlid || this.el.hasActiveFx()){
32662             Roo.callback(cb);
32663             return;
32664         }
32665         this.isSlid = false;
32666         this.beforeSlide();
32667         this.el.slideOut(this.getSlideAnchor(), {
32668             callback: function(){
32669                 this.el.setLeftTop(-10000, -10000);
32670                 this.afterSlide();
32671                 this.afterSlideIn();
32672                 Roo.callback(cb);
32673             },
32674             scope: this,
32675             block: true
32676         });
32677     },
32678     
32679     slideInIf : function(e){
32680         if(!e.within(this.el)){
32681             this.slideIn();
32682         }
32683     },
32684
32685     animateCollapse : function(){
32686         this.beforeSlide();
32687         this.el.setStyle("z-index", 20000);
32688         var anchor = this.getSlideAnchor();
32689         this.el.slideOut(anchor, {
32690             callback : function(){
32691                 this.el.setStyle("z-index", "");
32692                 this.collapsedEl.slideIn(anchor, {duration:.3});
32693                 this.afterSlide();
32694                 this.el.setLocation(-10000,-10000);
32695                 this.el.hide();
32696                 this.fireEvent("collapsed", this);
32697             },
32698             scope: this,
32699             block: true
32700         });
32701     },
32702
32703     animateExpand : function(){
32704         this.beforeSlide();
32705         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
32706         this.el.setStyle("z-index", 20000);
32707         this.collapsedEl.hide({
32708             duration:.1
32709         });
32710         this.el.slideIn(this.getSlideAnchor(), {
32711             callback : function(){
32712                 this.el.setStyle("z-index", "");
32713                 this.afterSlide();
32714                 if(this.split){
32715                     this.split.el.show();
32716                 }
32717                 this.fireEvent("invalidated", this);
32718                 this.fireEvent("expanded", this);
32719             },
32720             scope: this,
32721             block: true
32722         });
32723     },
32724
32725     anchors : {
32726         "west" : "left",
32727         "east" : "right",
32728         "north" : "top",
32729         "south" : "bottom"
32730     },
32731
32732     sanchors : {
32733         "west" : "l",
32734         "east" : "r",
32735         "north" : "t",
32736         "south" : "b"
32737     },
32738
32739     canchors : {
32740         "west" : "tl-tr",
32741         "east" : "tr-tl",
32742         "north" : "tl-bl",
32743         "south" : "bl-tl"
32744     },
32745
32746     getAnchor : function(){
32747         return this.anchors[this.position];
32748     },
32749
32750     getCollapseAnchor : function(){
32751         return this.canchors[this.position];
32752     },
32753
32754     getSlideAnchor : function(){
32755         return this.sanchors[this.position];
32756     },
32757
32758     getAlignAdj : function(){
32759         var cm = this.cmargins;
32760         switch(this.position){
32761             case "west":
32762                 return [0, 0];
32763             break;
32764             case "east":
32765                 return [0, 0];
32766             break;
32767             case "north":
32768                 return [0, 0];
32769             break;
32770             case "south":
32771                 return [0, 0];
32772             break;
32773         }
32774     },
32775
32776     getExpandAdj : function(){
32777         var c = this.collapsedEl, cm = this.cmargins;
32778         switch(this.position){
32779             case "west":
32780                 return [-(cm.right+c.getWidth()+cm.left), 0];
32781             break;
32782             case "east":
32783                 return [cm.right+c.getWidth()+cm.left, 0];
32784             break;
32785             case "north":
32786                 return [0, -(cm.top+cm.bottom+c.getHeight())];
32787             break;
32788             case "south":
32789                 return [0, cm.top+cm.bottom+c.getHeight()];
32790             break;
32791         }
32792     }
32793 });/*
32794  * Based on:
32795  * Ext JS Library 1.1.1
32796  * Copyright(c) 2006-2007, Ext JS, LLC.
32797  *
32798  * Originally Released Under LGPL - original licence link has changed is not relivant.
32799  *
32800  * Fork - LGPL
32801  * <script type="text/javascript">
32802  */
32803 /*
32804  * These classes are private internal classes
32805  */
32806 Roo.CenterLayoutRegion = function(mgr, config){
32807     Roo.LayoutRegion.call(this, mgr, config, "center");
32808     this.visible = true;
32809     this.minWidth = config.minWidth || 20;
32810     this.minHeight = config.minHeight || 20;
32811 };
32812
32813 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
32814     hide : function(){
32815         // center panel can't be hidden
32816     },
32817     
32818     show : function(){
32819         // center panel can't be hidden
32820     },
32821     
32822     getMinWidth: function(){
32823         return this.minWidth;
32824     },
32825     
32826     getMinHeight: function(){
32827         return this.minHeight;
32828     }
32829 });
32830
32831
32832 Roo.NorthLayoutRegion = function(mgr, config){
32833     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
32834     if(this.split){
32835         this.split.placement = Roo.SplitBar.TOP;
32836         this.split.orientation = Roo.SplitBar.VERTICAL;
32837         this.split.el.addClass("x-layout-split-v");
32838     }
32839     var size = config.initialSize || config.height;
32840     if(typeof size != "undefined"){
32841         this.el.setHeight(size);
32842     }
32843 };
32844 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
32845     orientation: Roo.SplitBar.VERTICAL,
32846     getBox : function(){
32847         if(this.collapsed){
32848             return this.collapsedEl.getBox();
32849         }
32850         var box = this.el.getBox();
32851         if(this.split){
32852             box.height += this.split.el.getHeight();
32853         }
32854         return box;
32855     },
32856     
32857     updateBox : function(box){
32858         if(this.split && !this.collapsed){
32859             box.height -= this.split.el.getHeight();
32860             this.split.el.setLeft(box.x);
32861             this.split.el.setTop(box.y+box.height);
32862             this.split.el.setWidth(box.width);
32863         }
32864         if(this.collapsed){
32865             this.updateBody(box.width, null);
32866         }
32867         Roo.LayoutRegion.prototype.updateBox.call(this, box);
32868     }
32869 });
32870
32871 Roo.SouthLayoutRegion = function(mgr, config){
32872     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
32873     if(this.split){
32874         this.split.placement = Roo.SplitBar.BOTTOM;
32875         this.split.orientation = Roo.SplitBar.VERTICAL;
32876         this.split.el.addClass("x-layout-split-v");
32877     }
32878     var size = config.initialSize || config.height;
32879     if(typeof size != "undefined"){
32880         this.el.setHeight(size);
32881     }
32882 };
32883 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
32884     orientation: Roo.SplitBar.VERTICAL,
32885     getBox : function(){
32886         if(this.collapsed){
32887             return this.collapsedEl.getBox();
32888         }
32889         var box = this.el.getBox();
32890         if(this.split){
32891             var sh = this.split.el.getHeight();
32892             box.height += sh;
32893             box.y -= sh;
32894         }
32895         return box;
32896     },
32897     
32898     updateBox : function(box){
32899         if(this.split && !this.collapsed){
32900             var sh = this.split.el.getHeight();
32901             box.height -= sh;
32902             box.y += sh;
32903             this.split.el.setLeft(box.x);
32904             this.split.el.setTop(box.y-sh);
32905             this.split.el.setWidth(box.width);
32906         }
32907         if(this.collapsed){
32908             this.updateBody(box.width, null);
32909         }
32910         Roo.LayoutRegion.prototype.updateBox.call(this, box);
32911     }
32912 });
32913
32914 Roo.EastLayoutRegion = function(mgr, config){
32915     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
32916     if(this.split){
32917         this.split.placement = Roo.SplitBar.RIGHT;
32918         this.split.orientation = Roo.SplitBar.HORIZONTAL;
32919         this.split.el.addClass("x-layout-split-h");
32920     }
32921     var size = config.initialSize || config.width;
32922     if(typeof size != "undefined"){
32923         this.el.setWidth(size);
32924     }
32925 };
32926 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
32927     orientation: Roo.SplitBar.HORIZONTAL,
32928     getBox : function(){
32929         if(this.collapsed){
32930             return this.collapsedEl.getBox();
32931         }
32932         var box = this.el.getBox();
32933         if(this.split){
32934             var sw = this.split.el.getWidth();
32935             box.width += sw;
32936             box.x -= sw;
32937         }
32938         return box;
32939     },
32940
32941     updateBox : function(box){
32942         if(this.split && !this.collapsed){
32943             var sw = this.split.el.getWidth();
32944             box.width -= sw;
32945             this.split.el.setLeft(box.x);
32946             this.split.el.setTop(box.y);
32947             this.split.el.setHeight(box.height);
32948             box.x += sw;
32949         }
32950         if(this.collapsed){
32951             this.updateBody(null, box.height);
32952         }
32953         Roo.LayoutRegion.prototype.updateBox.call(this, box);
32954     }
32955 });
32956
32957 Roo.WestLayoutRegion = function(mgr, config){
32958     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
32959     if(this.split){
32960         this.split.placement = Roo.SplitBar.LEFT;
32961         this.split.orientation = Roo.SplitBar.HORIZONTAL;
32962         this.split.el.addClass("x-layout-split-h");
32963     }
32964     var size = config.initialSize || config.width;
32965     if(typeof size != "undefined"){
32966         this.el.setWidth(size);
32967     }
32968 };
32969 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
32970     orientation: Roo.SplitBar.HORIZONTAL,
32971     getBox : function(){
32972         if(this.collapsed){
32973             return this.collapsedEl.getBox();
32974         }
32975         var box = this.el.getBox();
32976         if(this.split){
32977             box.width += this.split.el.getWidth();
32978         }
32979         return box;
32980     },
32981     
32982     updateBox : function(box){
32983         if(this.split && !this.collapsed){
32984             var sw = this.split.el.getWidth();
32985             box.width -= sw;
32986             this.split.el.setLeft(box.x+box.width);
32987             this.split.el.setTop(box.y);
32988             this.split.el.setHeight(box.height);
32989         }
32990         if(this.collapsed){
32991             this.updateBody(null, box.height);
32992         }
32993         Roo.LayoutRegion.prototype.updateBox.call(this, box);
32994     }
32995 });
32996 /*
32997  * Based on:
32998  * Ext JS Library 1.1.1
32999  * Copyright(c) 2006-2007, Ext JS, LLC.
33000  *
33001  * Originally Released Under LGPL - original licence link has changed is not relivant.
33002  *
33003  * Fork - LGPL
33004  * <script type="text/javascript">
33005  */
33006  
33007  
33008 /*
33009  * Private internal class for reading and applying state
33010  */
33011 Roo.LayoutStateManager = function(layout){
33012      // default empty state
33013      this.state = {
33014         north: {},
33015         south: {},
33016         east: {},
33017         west: {}       
33018     };
33019 };
33020
33021 Roo.LayoutStateManager.prototype = {
33022     init : function(layout, provider){
33023         this.provider = provider;
33024         var state = provider.get(layout.id+"-layout-state");
33025         if(state){
33026             var wasUpdating = layout.isUpdating();
33027             if(!wasUpdating){
33028                 layout.beginUpdate();
33029             }
33030             for(var key in state){
33031                 if(typeof state[key] != "function"){
33032                     var rstate = state[key];
33033                     var r = layout.getRegion(key);
33034                     if(r && rstate){
33035                         if(rstate.size){
33036                             r.resizeTo(rstate.size);
33037                         }
33038                         if(rstate.collapsed == true){
33039                             r.collapse(true);
33040                         }else{
33041                             r.expand(null, true);
33042                         }
33043                     }
33044                 }
33045             }
33046             if(!wasUpdating){
33047                 layout.endUpdate();
33048             }
33049             this.state = state; 
33050         }
33051         this.layout = layout;
33052         layout.on("regionresized", this.onRegionResized, this);
33053         layout.on("regioncollapsed", this.onRegionCollapsed, this);
33054         layout.on("regionexpanded", this.onRegionExpanded, this);
33055     },
33056     
33057     storeState : function(){
33058         this.provider.set(this.layout.id+"-layout-state", this.state);
33059     },
33060     
33061     onRegionResized : function(region, newSize){
33062         this.state[region.getPosition()].size = newSize;
33063         this.storeState();
33064     },
33065     
33066     onRegionCollapsed : function(region){
33067         this.state[region.getPosition()].collapsed = true;
33068         this.storeState();
33069     },
33070     
33071     onRegionExpanded : function(region){
33072         this.state[region.getPosition()].collapsed = false;
33073         this.storeState();
33074     }
33075 };/*
33076  * Based on:
33077  * Ext JS Library 1.1.1
33078  * Copyright(c) 2006-2007, Ext JS, LLC.
33079  *
33080  * Originally Released Under LGPL - original licence link has changed is not relivant.
33081  *
33082  * Fork - LGPL
33083  * <script type="text/javascript">
33084  */
33085 /**
33086  * @class Roo.ContentPanel
33087  * @extends Roo.util.Observable
33088  * A basic ContentPanel element.
33089  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
33090  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
33091  * @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
33092  * @cfg {Boolean}   closable      True if the panel can be closed/removed
33093  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
33094  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
33095  * @cfg {Toolbar}   toolbar       A toolbar for this panel
33096  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
33097  * @cfg {String} title          The title for this panel
33098  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
33099  * @cfg {String} url            Calls {@link #setUrl} with this value
33100  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
33101  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
33102  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
33103  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
33104
33105  * @constructor
33106  * Create a new ContentPanel.
33107  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
33108  * @param {String/Object} config A string to set only the title or a config object
33109  * @param {String} content (optional) Set the HTML content for this panel
33110  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
33111  */
33112 Roo.ContentPanel = function(el, config, content){
33113     
33114      
33115     /*
33116     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
33117         config = el;
33118         el = Roo.id();
33119     }
33120     if (config && config.parentLayout) { 
33121         el = config.parentLayout.el.createChild(); 
33122     }
33123     */
33124     if(el.autoCreate){ // xtype is available if this is called from factory
33125         config = el;
33126         el = Roo.id();
33127     }
33128     this.el = Roo.get(el);
33129     if(!this.el && config && config.autoCreate){
33130         if(typeof config.autoCreate == "object"){
33131             if(!config.autoCreate.id){
33132                 config.autoCreate.id = config.id||el;
33133             }
33134             this.el = Roo.DomHelper.append(document.body,
33135                         config.autoCreate, true);
33136         }else{
33137             this.el = Roo.DomHelper.append(document.body,
33138                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
33139         }
33140     }
33141     this.closable = false;
33142     this.loaded = false;
33143     this.active = false;
33144     if(typeof config == "string"){
33145         this.title = config;
33146     }else{
33147         Roo.apply(this, config);
33148     }
33149     
33150     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
33151         this.wrapEl = this.el.wrap();
33152         this.toolbar.container = this.el.insertSibling(false, 'before');
33153         this.toolbar = new Roo.Toolbar(this.toolbar);
33154     }
33155     
33156     // xtype created footer. - not sure if will work as we normally have to render first..
33157     if (this.footer && !this.footer.el && this.footer.xtype) {
33158         if (!this.wrapEl) {
33159             this.wrapEl = this.el.wrap();
33160         }
33161     
33162         this.footer.container = this.wrapEl.createChild();
33163          
33164         this.footer = Roo.factory(this.footer, Roo);
33165         
33166     }
33167     
33168     if(this.resizeEl){
33169         this.resizeEl = Roo.get(this.resizeEl, true);
33170     }else{
33171         this.resizeEl = this.el;
33172     }
33173     this.addEvents({
33174         /**
33175          * @event activate
33176          * Fires when this panel is activated. 
33177          * @param {Roo.ContentPanel} this
33178          */
33179         "activate" : true,
33180         /**
33181          * @event deactivate
33182          * Fires when this panel is activated. 
33183          * @param {Roo.ContentPanel} this
33184          */
33185         "deactivate" : true,
33186
33187         /**
33188          * @event resize
33189          * Fires when this panel is resized if fitToFrame is true.
33190          * @param {Roo.ContentPanel} this
33191          * @param {Number} width The width after any component adjustments
33192          * @param {Number} height The height after any component adjustments
33193          */
33194         "resize" : true,
33195         
33196          /**
33197          * @event render
33198          * Fires when this tab is created
33199          * @param {Roo.ContentPanel} this
33200          */
33201         "render" : true
33202         
33203         
33204         
33205     });
33206     if(this.autoScroll){
33207         this.resizeEl.setStyle("overflow", "auto");
33208     } else {
33209         // fix randome scrolling
33210         this.el.on('scroll', function() {
33211             Roo.log('fix random scolling');
33212             this.scrollTo('top',0); 
33213         });
33214     }
33215     content = content || this.content;
33216     if(content){
33217         this.setContent(content);
33218     }
33219     if(config && config.url){
33220         this.setUrl(this.url, this.params, this.loadOnce);
33221     }
33222     
33223     
33224     
33225     Roo.ContentPanel.superclass.constructor.call(this);
33226     
33227     this.fireEvent('render', this);
33228 };
33229
33230 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
33231     tabTip:'',
33232     setRegion : function(region){
33233         this.region = region;
33234         if(region){
33235            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
33236         }else{
33237            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
33238         } 
33239     },
33240     
33241     /**
33242      * Returns the toolbar for this Panel if one was configured. 
33243      * @return {Roo.Toolbar} 
33244      */
33245     getToolbar : function(){
33246         return this.toolbar;
33247     },
33248     
33249     setActiveState : function(active){
33250         this.active = active;
33251         if(!active){
33252             this.fireEvent("deactivate", this);
33253         }else{
33254             this.fireEvent("activate", this);
33255         }
33256     },
33257     /**
33258      * Updates this panel's element
33259      * @param {String} content The new content
33260      * @param {Boolean} loadScripts (optional) true to look for and process scripts
33261     */
33262     setContent : function(content, loadScripts){
33263         this.el.update(content, loadScripts);
33264     },
33265
33266     ignoreResize : function(w, h){
33267         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
33268             return true;
33269         }else{
33270             this.lastSize = {width: w, height: h};
33271             return false;
33272         }
33273     },
33274     /**
33275      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
33276      * @return {Roo.UpdateManager} The UpdateManager
33277      */
33278     getUpdateManager : function(){
33279         return this.el.getUpdateManager();
33280     },
33281      /**
33282      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
33283      * @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:
33284 <pre><code>
33285 panel.load({
33286     url: "your-url.php",
33287     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
33288     callback: yourFunction,
33289     scope: yourObject, //(optional scope)
33290     discardUrl: false,
33291     nocache: false,
33292     text: "Loading...",
33293     timeout: 30,
33294     scripts: false
33295 });
33296 </code></pre>
33297      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
33298      * 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.
33299      * @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}
33300      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
33301      * @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.
33302      * @return {Roo.ContentPanel} this
33303      */
33304     load : function(){
33305         var um = this.el.getUpdateManager();
33306         um.update.apply(um, arguments);
33307         return this;
33308     },
33309
33310
33311     /**
33312      * 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.
33313      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
33314      * @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)
33315      * @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)
33316      * @return {Roo.UpdateManager} The UpdateManager
33317      */
33318     setUrl : function(url, params, loadOnce){
33319         if(this.refreshDelegate){
33320             this.removeListener("activate", this.refreshDelegate);
33321         }
33322         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
33323         this.on("activate", this.refreshDelegate);
33324         return this.el.getUpdateManager();
33325     },
33326     
33327     _handleRefresh : function(url, params, loadOnce){
33328         if(!loadOnce || !this.loaded){
33329             var updater = this.el.getUpdateManager();
33330             updater.update(url, params, this._setLoaded.createDelegate(this));
33331         }
33332     },
33333     
33334     _setLoaded : function(){
33335         this.loaded = true;
33336     }, 
33337     
33338     /**
33339      * Returns this panel's id
33340      * @return {String} 
33341      */
33342     getId : function(){
33343         return this.el.id;
33344     },
33345     
33346     /** 
33347      * Returns this panel's element - used by regiosn to add.
33348      * @return {Roo.Element} 
33349      */
33350     getEl : function(){
33351         return this.wrapEl || this.el;
33352     },
33353     
33354     adjustForComponents : function(width, height)
33355     {
33356         Roo.log('adjustForComponents ');
33357         if(this.resizeEl != this.el){
33358             width -= this.el.getFrameWidth('lr');
33359             height -= this.el.getFrameWidth('tb');
33360         }
33361         if(this.toolbar){
33362             var te = this.toolbar.getEl();
33363             height -= te.getHeight();
33364             te.setWidth(width);
33365         }
33366         if(this.footer){
33367             var te = this.footer.getEl();
33368             Roo.log("footer:" + te.getHeight());
33369             
33370             height -= te.getHeight();
33371             te.setWidth(width);
33372         }
33373         
33374         
33375         if(this.adjustments){
33376             width += this.adjustments[0];
33377             height += this.adjustments[1];
33378         }
33379         return {"width": width, "height": height};
33380     },
33381     
33382     setSize : function(width, height){
33383         if(this.fitToFrame && !this.ignoreResize(width, height)){
33384             if(this.fitContainer && this.resizeEl != this.el){
33385                 this.el.setSize(width, height);
33386             }
33387             var size = this.adjustForComponents(width, height);
33388             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
33389             this.fireEvent('resize', this, size.width, size.height);
33390         }
33391     },
33392     
33393     /**
33394      * Returns this panel's title
33395      * @return {String} 
33396      */
33397     getTitle : function(){
33398         return this.title;
33399     },
33400     
33401     /**
33402      * Set this panel's title
33403      * @param {String} title
33404      */
33405     setTitle : function(title){
33406         this.title = title;
33407         if(this.region){
33408             this.region.updatePanelTitle(this, title);
33409         }
33410     },
33411     
33412     /**
33413      * Returns true is this panel was configured to be closable
33414      * @return {Boolean} 
33415      */
33416     isClosable : function(){
33417         return this.closable;
33418     },
33419     
33420     beforeSlide : function(){
33421         this.el.clip();
33422         this.resizeEl.clip();
33423     },
33424     
33425     afterSlide : function(){
33426         this.el.unclip();
33427         this.resizeEl.unclip();
33428     },
33429     
33430     /**
33431      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
33432      *   Will fail silently if the {@link #setUrl} method has not been called.
33433      *   This does not activate the panel, just updates its content.
33434      */
33435     refresh : function(){
33436         if(this.refreshDelegate){
33437            this.loaded = false;
33438            this.refreshDelegate();
33439         }
33440     },
33441     
33442     /**
33443      * Destroys this panel
33444      */
33445     destroy : function(){
33446         this.el.removeAllListeners();
33447         var tempEl = document.createElement("span");
33448         tempEl.appendChild(this.el.dom);
33449         tempEl.innerHTML = "";
33450         this.el.remove();
33451         this.el = null;
33452     },
33453     
33454     /**
33455      * form - if the content panel contains a form - this is a reference to it.
33456      * @type {Roo.form.Form}
33457      */
33458     form : false,
33459     /**
33460      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
33461      *    This contains a reference to it.
33462      * @type {Roo.View}
33463      */
33464     view : false,
33465     
33466       /**
33467      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
33468      * <pre><code>
33469
33470 layout.addxtype({
33471        xtype : 'Form',
33472        items: [ .... ]
33473    }
33474 );
33475
33476 </code></pre>
33477      * @param {Object} cfg Xtype definition of item to add.
33478      */
33479     
33480     addxtype : function(cfg) {
33481         // add form..
33482         if (cfg.xtype.match(/^Form$/)) {
33483             
33484             var el;
33485             //if (this.footer) {
33486             //    el = this.footer.container.insertSibling(false, 'before');
33487             //} else {
33488                 el = this.el.createChild();
33489             //}
33490
33491             this.form = new  Roo.form.Form(cfg);
33492             
33493             
33494             if ( this.form.allItems.length) this.form.render(el.dom);
33495             return this.form;
33496         }
33497         // should only have one of theses..
33498         if (['View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
33499             // views..
33500             cfg.el = this.el.appendChild(document.createElement("div"));
33501             // factory?
33502             
33503             var ret = new Roo.factory(cfg);
33504             ret.render && ret.render(false, ''); // render blank..
33505             this.view = ret;
33506             return ret;
33507         }
33508         return false;
33509     }
33510 });
33511
33512 /**
33513  * @class Roo.GridPanel
33514  * @extends Roo.ContentPanel
33515  * @constructor
33516  * Create a new GridPanel.
33517  * @param {Roo.grid.Grid} grid The grid for this panel
33518  * @param {String/Object} config A string to set only the panel's title, or a config object
33519  */
33520 Roo.GridPanel = function(grid, config){
33521     
33522   
33523     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
33524         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
33525         
33526     this.wrapper.dom.appendChild(grid.getGridEl().dom);
33527     
33528     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
33529     
33530     if(this.toolbar){
33531         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
33532     }
33533     // xtype created footer. - not sure if will work as we normally have to render first..
33534     if (this.footer && !this.footer.el && this.footer.xtype) {
33535         
33536         this.footer.container = this.grid.getView().getFooterPanel(true);
33537         this.footer.dataSource = this.grid.dataSource;
33538         this.footer = Roo.factory(this.footer, Roo);
33539         
33540     }
33541     
33542     grid.monitorWindowResize = false; // turn off autosizing
33543     grid.autoHeight = false;
33544     grid.autoWidth = false;
33545     this.grid = grid;
33546     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
33547 };
33548
33549 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
33550     getId : function(){
33551         return this.grid.id;
33552     },
33553     
33554     /**
33555      * Returns the grid for this panel
33556      * @return {Roo.grid.Grid} 
33557      */
33558     getGrid : function(){
33559         return this.grid;    
33560     },
33561     
33562     setSize : function(width, height){
33563         if(!this.ignoreResize(width, height)){
33564             var grid = this.grid;
33565             var size = this.adjustForComponents(width, height);
33566             grid.getGridEl().setSize(size.width, size.height);
33567             grid.autoSize();
33568         }
33569     },
33570     
33571     beforeSlide : function(){
33572         this.grid.getView().scroller.clip();
33573     },
33574     
33575     afterSlide : function(){
33576         this.grid.getView().scroller.unclip();
33577     },
33578     
33579     destroy : function(){
33580         this.grid.destroy();
33581         delete this.grid;
33582         Roo.GridPanel.superclass.destroy.call(this); 
33583     }
33584 });
33585
33586
33587 /**
33588  * @class Roo.NestedLayoutPanel
33589  * @extends Roo.ContentPanel
33590  * @constructor
33591  * Create a new NestedLayoutPanel.
33592  * 
33593  * 
33594  * @param {Roo.BorderLayout} layout The layout for this panel
33595  * @param {String/Object} config A string to set only the title or a config object
33596  */
33597 Roo.NestedLayoutPanel = function(layout, config)
33598 {
33599     // construct with only one argument..
33600     /* FIXME - implement nicer consturctors
33601     if (layout.layout) {
33602         config = layout;
33603         layout = config.layout;
33604         delete config.layout;
33605     }
33606     if (layout.xtype && !layout.getEl) {
33607         // then layout needs constructing..
33608         layout = Roo.factory(layout, Roo);
33609     }
33610     */
33611     
33612     
33613     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
33614     
33615     layout.monitorWindowResize = false; // turn off autosizing
33616     this.layout = layout;
33617     this.layout.getEl().addClass("x-layout-nested-layout");
33618     
33619     
33620     
33621     
33622 };
33623
33624 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
33625
33626     setSize : function(width, height){
33627         if(!this.ignoreResize(width, height)){
33628             var size = this.adjustForComponents(width, height);
33629             var el = this.layout.getEl();
33630             el.setSize(size.width, size.height);
33631             var touch = el.dom.offsetWidth;
33632             this.layout.layout();
33633             // ie requires a double layout on the first pass
33634             if(Roo.isIE && !this.initialized){
33635                 this.initialized = true;
33636                 this.layout.layout();
33637             }
33638         }
33639     },
33640     
33641     // activate all subpanels if not currently active..
33642     
33643     setActiveState : function(active){
33644         this.active = active;
33645         if(!active){
33646             this.fireEvent("deactivate", this);
33647             return;
33648         }
33649         
33650         this.fireEvent("activate", this);
33651         // not sure if this should happen before or after..
33652         if (!this.layout) {
33653             return; // should not happen..
33654         }
33655         var reg = false;
33656         for (var r in this.layout.regions) {
33657             reg = this.layout.getRegion(r);
33658             if (reg.getActivePanel()) {
33659                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
33660                 reg.setActivePanel(reg.getActivePanel());
33661                 continue;
33662             }
33663             if (!reg.panels.length) {
33664                 continue;
33665             }
33666             reg.showPanel(reg.getPanel(0));
33667         }
33668         
33669         
33670         
33671         
33672     },
33673     
33674     /**
33675      * Returns the nested BorderLayout for this panel
33676      * @return {Roo.BorderLayout} 
33677      */
33678     getLayout : function(){
33679         return this.layout;
33680     },
33681     
33682      /**
33683      * Adds a xtype elements to the layout of the nested panel
33684      * <pre><code>
33685
33686 panel.addxtype({
33687        xtype : 'ContentPanel',
33688        region: 'west',
33689        items: [ .... ]
33690    }
33691 );
33692
33693 panel.addxtype({
33694         xtype : 'NestedLayoutPanel',
33695         region: 'west',
33696         layout: {
33697            center: { },
33698            west: { }   
33699         },
33700         items : [ ... list of content panels or nested layout panels.. ]
33701    }
33702 );
33703 </code></pre>
33704      * @param {Object} cfg Xtype definition of item to add.
33705      */
33706     addxtype : function(cfg) {
33707         return this.layout.addxtype(cfg);
33708     
33709     }
33710 });
33711
33712 Roo.ScrollPanel = function(el, config, content){
33713     config = config || {};
33714     config.fitToFrame = true;
33715     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
33716     
33717     this.el.dom.style.overflow = "hidden";
33718     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
33719     this.el.removeClass("x-layout-inactive-content");
33720     this.el.on("mousewheel", this.onWheel, this);
33721
33722     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
33723     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
33724     up.unselectable(); down.unselectable();
33725     up.on("click", this.scrollUp, this);
33726     down.on("click", this.scrollDown, this);
33727     up.addClassOnOver("x-scroller-btn-over");
33728     down.addClassOnOver("x-scroller-btn-over");
33729     up.addClassOnClick("x-scroller-btn-click");
33730     down.addClassOnClick("x-scroller-btn-click");
33731     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
33732
33733     this.resizeEl = this.el;
33734     this.el = wrap; this.up = up; this.down = down;
33735 };
33736
33737 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
33738     increment : 100,
33739     wheelIncrement : 5,
33740     scrollUp : function(){
33741         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
33742     },
33743
33744     scrollDown : function(){
33745         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
33746     },
33747
33748     afterScroll : function(){
33749         var el = this.resizeEl;
33750         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
33751         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
33752         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
33753     },
33754
33755     setSize : function(){
33756         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
33757         this.afterScroll();
33758     },
33759
33760     onWheel : function(e){
33761         var d = e.getWheelDelta();
33762         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
33763         this.afterScroll();
33764         e.stopEvent();
33765     },
33766
33767     setContent : function(content, loadScripts){
33768         this.resizeEl.update(content, loadScripts);
33769     }
33770
33771 });
33772
33773
33774
33775
33776
33777
33778
33779
33780
33781 /**
33782  * @class Roo.TreePanel
33783  * @extends Roo.ContentPanel
33784  * @constructor
33785  * Create a new TreePanel. - defaults to fit/scoll contents.
33786  * @param {String/Object} config A string to set only the panel's title, or a config object
33787  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
33788  */
33789 Roo.TreePanel = function(config){
33790     var el = config.el;
33791     var tree = config.tree;
33792     delete config.tree; 
33793     delete config.el; // hopefull!
33794     
33795     // wrapper for IE7 strict & safari scroll issue
33796     
33797     var treeEl = el.createChild();
33798     config.resizeEl = treeEl;
33799     
33800     
33801     
33802     Roo.TreePanel.superclass.constructor.call(this, el, config);
33803  
33804  
33805     this.tree = new Roo.tree.TreePanel(treeEl , tree);
33806     //console.log(tree);
33807     this.on('activate', function()
33808     {
33809         if (this.tree.rendered) {
33810             return;
33811         }
33812         //console.log('render tree');
33813         this.tree.render();
33814     });
33815     // this should not be needed.. - it's actually the 'el' that resizes?
33816     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
33817     
33818     //this.on('resize',  function (cp, w, h) {
33819     //        this.tree.innerCt.setWidth(w);
33820     //        this.tree.innerCt.setHeight(h);
33821     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
33822     //});
33823
33824         
33825     
33826 };
33827
33828 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
33829     fitToFrame : true,
33830     autoScroll : true
33831 });
33832
33833
33834
33835
33836
33837
33838
33839
33840
33841
33842
33843 /*
33844  * Based on:
33845  * Ext JS Library 1.1.1
33846  * Copyright(c) 2006-2007, Ext JS, LLC.
33847  *
33848  * Originally Released Under LGPL - original licence link has changed is not relivant.
33849  *
33850  * Fork - LGPL
33851  * <script type="text/javascript">
33852  */
33853  
33854
33855 /**
33856  * @class Roo.ReaderLayout
33857  * @extends Roo.BorderLayout
33858  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
33859  * center region containing two nested regions (a top one for a list view and one for item preview below),
33860  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
33861  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
33862  * expedites the setup of the overall layout and regions for this common application style.
33863  * Example:
33864  <pre><code>
33865 var reader = new Roo.ReaderLayout();
33866 var CP = Roo.ContentPanel;  // shortcut for adding
33867
33868 reader.beginUpdate();
33869 reader.add("north", new CP("north", "North"));
33870 reader.add("west", new CP("west", {title: "West"}));
33871 reader.add("east", new CP("east", {title: "East"}));
33872
33873 reader.regions.listView.add(new CP("listView", "List"));
33874 reader.regions.preview.add(new CP("preview", "Preview"));
33875 reader.endUpdate();
33876 </code></pre>
33877 * @constructor
33878 * Create a new ReaderLayout
33879 * @param {Object} config Configuration options
33880 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
33881 * document.body if omitted)
33882 */
33883 Roo.ReaderLayout = function(config, renderTo){
33884     var c = config || {size:{}};
33885     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
33886         north: c.north !== false ? Roo.apply({
33887             split:false,
33888             initialSize: 32,
33889             titlebar: false
33890         }, c.north) : false,
33891         west: c.west !== false ? Roo.apply({
33892             split:true,
33893             initialSize: 200,
33894             minSize: 175,
33895             maxSize: 400,
33896             titlebar: true,
33897             collapsible: true,
33898             animate: true,
33899             margins:{left:5,right:0,bottom:5,top:5},
33900             cmargins:{left:5,right:5,bottom:5,top:5}
33901         }, c.west) : false,
33902         east: c.east !== false ? Roo.apply({
33903             split:true,
33904             initialSize: 200,
33905             minSize: 175,
33906             maxSize: 400,
33907             titlebar: true,
33908             collapsible: true,
33909             animate: true,
33910             margins:{left:0,right:5,bottom:5,top:5},
33911             cmargins:{left:5,right:5,bottom:5,top:5}
33912         }, c.east) : false,
33913         center: Roo.apply({
33914             tabPosition: 'top',
33915             autoScroll:false,
33916             closeOnTab: true,
33917             titlebar:false,
33918             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
33919         }, c.center)
33920     });
33921
33922     this.el.addClass('x-reader');
33923
33924     this.beginUpdate();
33925
33926     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
33927         south: c.preview !== false ? Roo.apply({
33928             split:true,
33929             initialSize: 200,
33930             minSize: 100,
33931             autoScroll:true,
33932             collapsible:true,
33933             titlebar: true,
33934             cmargins:{top:5,left:0, right:0, bottom:0}
33935         }, c.preview) : false,
33936         center: Roo.apply({
33937             autoScroll:false,
33938             titlebar:false,
33939             minHeight:200
33940         }, c.listView)
33941     });
33942     this.add('center', new Roo.NestedLayoutPanel(inner,
33943             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
33944
33945     this.endUpdate();
33946
33947     this.regions.preview = inner.getRegion('south');
33948     this.regions.listView = inner.getRegion('center');
33949 };
33950
33951 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
33952  * Based on:
33953  * Ext JS Library 1.1.1
33954  * Copyright(c) 2006-2007, Ext JS, LLC.
33955  *
33956  * Originally Released Under LGPL - original licence link has changed is not relivant.
33957  *
33958  * Fork - LGPL
33959  * <script type="text/javascript">
33960  */
33961  
33962 /**
33963  * @class Roo.grid.Grid
33964  * @extends Roo.util.Observable
33965  * This class represents the primary interface of a component based grid control.
33966  * <br><br>Usage:<pre><code>
33967  var grid = new Roo.grid.Grid("my-container-id", {
33968      ds: myDataStore,
33969      cm: myColModel,
33970      selModel: mySelectionModel,
33971      autoSizeColumns: true,
33972      monitorWindowResize: false,
33973      trackMouseOver: true
33974  });
33975  // set any options
33976  grid.render();
33977  * </code></pre>
33978  * <b>Common Problems:</b><br/>
33979  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
33980  * element will correct this<br/>
33981  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
33982  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
33983  * are unpredictable.<br/>
33984  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
33985  * grid to calculate dimensions/offsets.<br/>
33986   * @constructor
33987  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
33988  * The container MUST have some type of size defined for the grid to fill. The container will be
33989  * automatically set to position relative if it isn't already.
33990  * @param {Object} config A config object that sets properties on this grid.
33991  */
33992 Roo.grid.Grid = function(container, config){
33993         // initialize the container
33994         this.container = Roo.get(container);
33995         this.container.update("");
33996         this.container.setStyle("overflow", "hidden");
33997     this.container.addClass('x-grid-container');
33998
33999     this.id = this.container.id;
34000
34001     Roo.apply(this, config);
34002     // check and correct shorthanded configs
34003     if(this.ds){
34004         this.dataSource = this.ds;
34005         delete this.ds;
34006     }
34007     if(this.cm){
34008         this.colModel = this.cm;
34009         delete this.cm;
34010     }
34011     if(this.sm){
34012         this.selModel = this.sm;
34013         delete this.sm;
34014     }
34015
34016     if (this.selModel) {
34017         this.selModel = Roo.factory(this.selModel, Roo.grid);
34018         this.sm = this.selModel;
34019         this.sm.xmodule = this.xmodule || false;
34020     }
34021     if (typeof(this.colModel.config) == 'undefined') {
34022         this.colModel = new Roo.grid.ColumnModel(this.colModel);
34023         this.cm = this.colModel;
34024         this.cm.xmodule = this.xmodule || false;
34025     }
34026     if (this.dataSource) {
34027         this.dataSource= Roo.factory(this.dataSource, Roo.data);
34028         this.ds = this.dataSource;
34029         this.ds.xmodule = this.xmodule || false;
34030          
34031     }
34032     
34033     
34034     
34035     if(this.width){
34036         this.container.setWidth(this.width);
34037     }
34038
34039     if(this.height){
34040         this.container.setHeight(this.height);
34041     }
34042     /** @private */
34043         this.addEvents({
34044         // raw events
34045         /**
34046          * @event click
34047          * The raw click event for the entire grid.
34048          * @param {Roo.EventObject} e
34049          */
34050         "click" : true,
34051         /**
34052          * @event dblclick
34053          * The raw dblclick event for the entire grid.
34054          * @param {Roo.EventObject} e
34055          */
34056         "dblclick" : true,
34057         /**
34058          * @event contextmenu
34059          * The raw contextmenu event for the entire grid.
34060          * @param {Roo.EventObject} e
34061          */
34062         "contextmenu" : true,
34063         /**
34064          * @event mousedown
34065          * The raw mousedown event for the entire grid.
34066          * @param {Roo.EventObject} e
34067          */
34068         "mousedown" : true,
34069         /**
34070          * @event mouseup
34071          * The raw mouseup event for the entire grid.
34072          * @param {Roo.EventObject} e
34073          */
34074         "mouseup" : true,
34075         /**
34076          * @event mouseover
34077          * The raw mouseover event for the entire grid.
34078          * @param {Roo.EventObject} e
34079          */
34080         "mouseover" : true,
34081         /**
34082          * @event mouseout
34083          * The raw mouseout event for the entire grid.
34084          * @param {Roo.EventObject} e
34085          */
34086         "mouseout" : true,
34087         /**
34088          * @event keypress
34089          * The raw keypress event for the entire grid.
34090          * @param {Roo.EventObject} e
34091          */
34092         "keypress" : true,
34093         /**
34094          * @event keydown
34095          * The raw keydown event for the entire grid.
34096          * @param {Roo.EventObject} e
34097          */
34098         "keydown" : true,
34099
34100         // custom events
34101
34102         /**
34103          * @event cellclick
34104          * Fires when a cell is clicked
34105          * @param {Grid} this
34106          * @param {Number} rowIndex
34107          * @param {Number} columnIndex
34108          * @param {Roo.EventObject} e
34109          */
34110         "cellclick" : true,
34111         /**
34112          * @event celldblclick
34113          * Fires when a cell is double clicked
34114          * @param {Grid} this
34115          * @param {Number} rowIndex
34116          * @param {Number} columnIndex
34117          * @param {Roo.EventObject} e
34118          */
34119         "celldblclick" : true,
34120         /**
34121          * @event rowclick
34122          * Fires when a row is clicked
34123          * @param {Grid} this
34124          * @param {Number} rowIndex
34125          * @param {Roo.EventObject} e
34126          */
34127         "rowclick" : true,
34128         /**
34129          * @event rowdblclick
34130          * Fires when a row is double clicked
34131          * @param {Grid} this
34132          * @param {Number} rowIndex
34133          * @param {Roo.EventObject} e
34134          */
34135         "rowdblclick" : true,
34136         /**
34137          * @event headerclick
34138          * Fires when a header is clicked
34139          * @param {Grid} this
34140          * @param {Number} columnIndex
34141          * @param {Roo.EventObject} e
34142          */
34143         "headerclick" : true,
34144         /**
34145          * @event headerdblclick
34146          * Fires when a header cell is double clicked
34147          * @param {Grid} this
34148          * @param {Number} columnIndex
34149          * @param {Roo.EventObject} e
34150          */
34151         "headerdblclick" : true,
34152         /**
34153          * @event rowcontextmenu
34154          * Fires when a row is right clicked
34155          * @param {Grid} this
34156          * @param {Number} rowIndex
34157          * @param {Roo.EventObject} e
34158          */
34159         "rowcontextmenu" : true,
34160         /**
34161          * @event cellcontextmenu
34162          * Fires when a cell is right clicked
34163          * @param {Grid} this
34164          * @param {Number} rowIndex
34165          * @param {Number} cellIndex
34166          * @param {Roo.EventObject} e
34167          */
34168          "cellcontextmenu" : true,
34169         /**
34170          * @event headercontextmenu
34171          * Fires when a header is right clicked
34172          * @param {Grid} this
34173          * @param {Number} columnIndex
34174          * @param {Roo.EventObject} e
34175          */
34176         "headercontextmenu" : true,
34177         /**
34178          * @event bodyscroll
34179          * Fires when the body element is scrolled
34180          * @param {Number} scrollLeft
34181          * @param {Number} scrollTop
34182          */
34183         "bodyscroll" : true,
34184         /**
34185          * @event columnresize
34186          * Fires when the user resizes a column
34187          * @param {Number} columnIndex
34188          * @param {Number} newSize
34189          */
34190         "columnresize" : true,
34191         /**
34192          * @event columnmove
34193          * Fires when the user moves a column
34194          * @param {Number} oldIndex
34195          * @param {Number} newIndex
34196          */
34197         "columnmove" : true,
34198         /**
34199          * @event startdrag
34200          * Fires when row(s) start being dragged
34201          * @param {Grid} this
34202          * @param {Roo.GridDD} dd The drag drop object
34203          * @param {event} e The raw browser event
34204          */
34205         "startdrag" : true,
34206         /**
34207          * @event enddrag
34208          * Fires when a drag operation is complete
34209          * @param {Grid} this
34210          * @param {Roo.GridDD} dd The drag drop object
34211          * @param {event} e The raw browser event
34212          */
34213         "enddrag" : true,
34214         /**
34215          * @event dragdrop
34216          * Fires when dragged row(s) are dropped on a valid DD target
34217          * @param {Grid} this
34218          * @param {Roo.GridDD} dd The drag drop object
34219          * @param {String} targetId The target drag drop object
34220          * @param {event} e The raw browser event
34221          */
34222         "dragdrop" : true,
34223         /**
34224          * @event dragover
34225          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
34226          * @param {Grid} this
34227          * @param {Roo.GridDD} dd The drag drop object
34228          * @param {String} targetId The target drag drop object
34229          * @param {event} e The raw browser event
34230          */
34231         "dragover" : true,
34232         /**
34233          * @event dragenter
34234          *  Fires when the dragged row(s) first cross another DD target while being dragged
34235          * @param {Grid} this
34236          * @param {Roo.GridDD} dd The drag drop object
34237          * @param {String} targetId The target drag drop object
34238          * @param {event} e The raw browser event
34239          */
34240         "dragenter" : true,
34241         /**
34242          * @event dragout
34243          * Fires when the dragged row(s) leave another DD target while being dragged
34244          * @param {Grid} this
34245          * @param {Roo.GridDD} dd The drag drop object
34246          * @param {String} targetId The target drag drop object
34247          * @param {event} e The raw browser event
34248          */
34249         "dragout" : true,
34250         /**
34251          * @event rowclass
34252          * Fires when a row is rendered, so you can change add a style to it.
34253          * @param {GridView} gridview   The grid view
34254          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
34255          */
34256         'rowclass' : true,
34257
34258         /**
34259          * @event render
34260          * Fires when the grid is rendered
34261          * @param {Grid} grid
34262          */
34263         'render' : true
34264     });
34265
34266     Roo.grid.Grid.superclass.constructor.call(this);
34267 };
34268 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
34269     
34270     /**
34271      * @cfg {String} ddGroup - drag drop group.
34272      */
34273
34274     /**
34275      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
34276      */
34277     minColumnWidth : 25,
34278
34279     /**
34280      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
34281      * <b>on initial render.</b> It is more efficient to explicitly size the columns
34282      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
34283      */
34284     autoSizeColumns : false,
34285
34286     /**
34287      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
34288      */
34289     autoSizeHeaders : true,
34290
34291     /**
34292      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
34293      */
34294     monitorWindowResize : true,
34295
34296     /**
34297      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
34298      * rows measured to get a columns size. Default is 0 (all rows).
34299      */
34300     maxRowsToMeasure : 0,
34301
34302     /**
34303      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
34304      */
34305     trackMouseOver : true,
34306
34307     /**
34308     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
34309     */
34310     
34311     /**
34312     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
34313     */
34314     enableDragDrop : false,
34315     
34316     /**
34317     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
34318     */
34319     enableColumnMove : true,
34320     
34321     /**
34322     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
34323     */
34324     enableColumnHide : true,
34325     
34326     /**
34327     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
34328     */
34329     enableRowHeightSync : false,
34330     
34331     /**
34332     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
34333     */
34334     stripeRows : true,
34335     
34336     /**
34337     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
34338     */
34339     autoHeight : false,
34340
34341     /**
34342      * @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.
34343      */
34344     autoExpandColumn : false,
34345
34346     /**
34347     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
34348     * Default is 50.
34349     */
34350     autoExpandMin : 50,
34351
34352     /**
34353     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
34354     */
34355     autoExpandMax : 1000,
34356
34357     /**
34358     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
34359     */
34360     view : null,
34361
34362     /**
34363     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
34364     */
34365     loadMask : false,
34366     /**
34367     * @cfg {Roo.dd.DropTarget} dragTarget An {@link Roo.dd.DragTarget} config
34368     */
34369     dropTarget: false,
34370     
34371    
34372     
34373     // private
34374     rendered : false,
34375
34376     /**
34377     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
34378     * of a fixed width. Default is false.
34379     */
34380     /**
34381     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
34382     */
34383     /**
34384      * Called once after all setup has been completed and the grid is ready to be rendered.
34385      * @return {Roo.grid.Grid} this
34386      */
34387     render : function()
34388     {
34389         var c = this.container;
34390         // try to detect autoHeight/width mode
34391         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
34392             this.autoHeight = true;
34393         }
34394         var view = this.getView();
34395         view.init(this);
34396
34397         c.on("click", this.onClick, this);
34398         c.on("dblclick", this.onDblClick, this);
34399         c.on("contextmenu", this.onContextMenu, this);
34400         c.on("keydown", this.onKeyDown, this);
34401
34402         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
34403
34404         this.getSelectionModel().init(this);
34405
34406         view.render();
34407
34408         if(this.loadMask){
34409             this.loadMask = new Roo.LoadMask(this.container,
34410                     Roo.apply({store:this.dataSource}, this.loadMask));
34411         }
34412         
34413         
34414         if (this.toolbar && this.toolbar.xtype) {
34415             this.toolbar.container = this.getView().getHeaderPanel(true);
34416             this.toolbar = new Roo.Toolbar(this.toolbar);
34417         }
34418         if (this.footer && this.footer.xtype) {
34419             this.footer.dataSource = this.getDataSource();
34420             this.footer.container = this.getView().getFooterPanel(true);
34421             this.footer = Roo.factory(this.footer, Roo);
34422         }
34423         if (this.dropTarget && this.dropTarget.xtype) {
34424             delete this.dropTarget.xtype;
34425             this.dropTarget =  new Ext.dd.DropTarget(this.getView().mainBody, this.dropTarget);
34426         }
34427         
34428         
34429         this.rendered = true;
34430         this.fireEvent('render', this);
34431         return this;
34432     },
34433
34434         /**
34435          * Reconfigures the grid to use a different Store and Column Model.
34436          * The View will be bound to the new objects and refreshed.
34437          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
34438          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
34439          */
34440     reconfigure : function(dataSource, colModel){
34441         if(this.loadMask){
34442             this.loadMask.destroy();
34443             this.loadMask = new Roo.LoadMask(this.container,
34444                     Roo.apply({store:dataSource}, this.loadMask));
34445         }
34446         this.view.bind(dataSource, colModel);
34447         this.dataSource = dataSource;
34448         this.colModel = colModel;
34449         this.view.refresh(true);
34450     },
34451
34452     // private
34453     onKeyDown : function(e){
34454         this.fireEvent("keydown", e);
34455     },
34456
34457     /**
34458      * Destroy this grid.
34459      * @param {Boolean} removeEl True to remove the element
34460      */
34461     destroy : function(removeEl, keepListeners){
34462         if(this.loadMask){
34463             this.loadMask.destroy();
34464         }
34465         var c = this.container;
34466         c.removeAllListeners();
34467         this.view.destroy();
34468         this.colModel.purgeListeners();
34469         if(!keepListeners){
34470             this.purgeListeners();
34471         }
34472         c.update("");
34473         if(removeEl === true){
34474             c.remove();
34475         }
34476     },
34477
34478     // private
34479     processEvent : function(name, e){
34480         this.fireEvent(name, e);
34481         var t = e.getTarget();
34482         var v = this.view;
34483         var header = v.findHeaderIndex(t);
34484         if(header !== false){
34485             this.fireEvent("header" + name, this, header, e);
34486         }else{
34487             var row = v.findRowIndex(t);
34488             var cell = v.findCellIndex(t);
34489             if(row !== false){
34490                 this.fireEvent("row" + name, this, row, e);
34491                 if(cell !== false){
34492                     this.fireEvent("cell" + name, this, row, cell, e);
34493                 }
34494             }
34495         }
34496     },
34497
34498     // private
34499     onClick : function(e){
34500         this.processEvent("click", e);
34501     },
34502
34503     // private
34504     onContextMenu : function(e, t){
34505         this.processEvent("contextmenu", e);
34506     },
34507
34508     // private
34509     onDblClick : function(e){
34510         this.processEvent("dblclick", e);
34511     },
34512
34513     // private
34514     walkCells : function(row, col, step, fn, scope){
34515         var cm = this.colModel, clen = cm.getColumnCount();
34516         var ds = this.dataSource, rlen = ds.getCount(), first = true;
34517         if(step < 0){
34518             if(col < 0){
34519                 row--;
34520                 first = false;
34521             }
34522             while(row >= 0){
34523                 if(!first){
34524                     col = clen-1;
34525                 }
34526                 first = false;
34527                 while(col >= 0){
34528                     if(fn.call(scope || this, row, col, cm) === true){
34529                         return [row, col];
34530                     }
34531                     col--;
34532                 }
34533                 row--;
34534             }
34535         } else {
34536             if(col >= clen){
34537                 row++;
34538                 first = false;
34539             }
34540             while(row < rlen){
34541                 if(!first){
34542                     col = 0;
34543                 }
34544                 first = false;
34545                 while(col < clen){
34546                     if(fn.call(scope || this, row, col, cm) === true){
34547                         return [row, col];
34548                     }
34549                     col++;
34550                 }
34551                 row++;
34552             }
34553         }
34554         return null;
34555     },
34556
34557     // private
34558     getSelections : function(){
34559         return this.selModel.getSelections();
34560     },
34561
34562     /**
34563      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
34564      * but if manual update is required this method will initiate it.
34565      */
34566     autoSize : function(){
34567         if(this.rendered){
34568             this.view.layout();
34569             if(this.view.adjustForScroll){
34570                 this.view.adjustForScroll();
34571             }
34572         }
34573     },
34574
34575     /**
34576      * Returns the grid's underlying element.
34577      * @return {Element} The element
34578      */
34579     getGridEl : function(){
34580         return this.container;
34581     },
34582
34583     // private for compatibility, overridden by editor grid
34584     stopEditing : function(){},
34585
34586     /**
34587      * Returns the grid's SelectionModel.
34588      * @return {SelectionModel}
34589      */
34590     getSelectionModel : function(){
34591         if(!this.selModel){
34592             this.selModel = new Roo.grid.RowSelectionModel();
34593         }
34594         return this.selModel;
34595     },
34596
34597     /**
34598      * Returns the grid's DataSource.
34599      * @return {DataSource}
34600      */
34601     getDataSource : function(){
34602         return this.dataSource;
34603     },
34604
34605     /**
34606      * Returns the grid's ColumnModel.
34607      * @return {ColumnModel}
34608      */
34609     getColumnModel : function(){
34610         return this.colModel;
34611     },
34612
34613     /**
34614      * Returns the grid's GridView object.
34615      * @return {GridView}
34616      */
34617     getView : function(){
34618         if(!this.view){
34619             this.view = new Roo.grid.GridView(this.viewConfig);
34620         }
34621         return this.view;
34622     },
34623     /**
34624      * Called to get grid's drag proxy text, by default returns this.ddText.
34625      * @return {String}
34626      */
34627     getDragDropText : function(){
34628         var count = this.selModel.getCount();
34629         return String.format(this.ddText, count, count == 1 ? '' : 's');
34630     }
34631 });
34632 /**
34633  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
34634  * %0 is replaced with the number of selected rows.
34635  * @type String
34636  */
34637 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
34638  * Based on:
34639  * Ext JS Library 1.1.1
34640  * Copyright(c) 2006-2007, Ext JS, LLC.
34641  *
34642  * Originally Released Under LGPL - original licence link has changed is not relivant.
34643  *
34644  * Fork - LGPL
34645  * <script type="text/javascript">
34646  */
34647  
34648 Roo.grid.AbstractGridView = function(){
34649         this.grid = null;
34650         
34651         this.events = {
34652             "beforerowremoved" : true,
34653             "beforerowsinserted" : true,
34654             "beforerefresh" : true,
34655             "rowremoved" : true,
34656             "rowsinserted" : true,
34657             "rowupdated" : true,
34658             "refresh" : true
34659         };
34660     Roo.grid.AbstractGridView.superclass.constructor.call(this);
34661 };
34662
34663 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
34664     rowClass : "x-grid-row",
34665     cellClass : "x-grid-cell",
34666     tdClass : "x-grid-td",
34667     hdClass : "x-grid-hd",
34668     splitClass : "x-grid-hd-split",
34669     
34670         init: function(grid){
34671         this.grid = grid;
34672                 var cid = this.grid.getGridEl().id;
34673         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
34674         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
34675         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
34676         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
34677         },
34678         
34679         getColumnRenderers : function(){
34680         var renderers = [];
34681         var cm = this.grid.colModel;
34682         var colCount = cm.getColumnCount();
34683         for(var i = 0; i < colCount; i++){
34684             renderers[i] = cm.getRenderer(i);
34685         }
34686         return renderers;
34687     },
34688     
34689     getColumnIds : function(){
34690         var ids = [];
34691         var cm = this.grid.colModel;
34692         var colCount = cm.getColumnCount();
34693         for(var i = 0; i < colCount; i++){
34694             ids[i] = cm.getColumnId(i);
34695         }
34696         return ids;
34697     },
34698     
34699     getDataIndexes : function(){
34700         if(!this.indexMap){
34701             this.indexMap = this.buildIndexMap();
34702         }
34703         return this.indexMap.colToData;
34704     },
34705     
34706     getColumnIndexByDataIndex : function(dataIndex){
34707         if(!this.indexMap){
34708             this.indexMap = this.buildIndexMap();
34709         }
34710         return this.indexMap.dataToCol[dataIndex];
34711     },
34712     
34713     /**
34714      * Set a css style for a column dynamically. 
34715      * @param {Number} colIndex The index of the column
34716      * @param {String} name The css property name
34717      * @param {String} value The css value
34718      */
34719     setCSSStyle : function(colIndex, name, value){
34720         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
34721         Roo.util.CSS.updateRule(selector, name, value);
34722     },
34723     
34724     generateRules : function(cm){
34725         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
34726         Roo.util.CSS.removeStyleSheet(rulesId);
34727         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34728             var cid = cm.getColumnId(i);
34729             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
34730                          this.tdSelector, cid, " {\n}\n",
34731                          this.hdSelector, cid, " {\n}\n",
34732                          this.splitSelector, cid, " {\n}\n");
34733         }
34734         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
34735     }
34736 });/*
34737  * Based on:
34738  * Ext JS Library 1.1.1
34739  * Copyright(c) 2006-2007, Ext JS, LLC.
34740  *
34741  * Originally Released Under LGPL - original licence link has changed is not relivant.
34742  *
34743  * Fork - LGPL
34744  * <script type="text/javascript">
34745  */
34746
34747 // private
34748 // This is a support class used internally by the Grid components
34749 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
34750     this.grid = grid;
34751     this.view = grid.getView();
34752     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
34753     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
34754     if(hd2){
34755         this.setHandleElId(Roo.id(hd));
34756         this.setOuterHandleElId(Roo.id(hd2));
34757     }
34758     this.scroll = false;
34759 };
34760 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
34761     maxDragWidth: 120,
34762     getDragData : function(e){
34763         var t = Roo.lib.Event.getTarget(e);
34764         var h = this.view.findHeaderCell(t);
34765         if(h){
34766             return {ddel: h.firstChild, header:h};
34767         }
34768         return false;
34769     },
34770
34771     onInitDrag : function(e){
34772         this.view.headersDisabled = true;
34773         var clone = this.dragData.ddel.cloneNode(true);
34774         clone.id = Roo.id();
34775         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
34776         this.proxy.update(clone);
34777         return true;
34778     },
34779
34780     afterValidDrop : function(){
34781         var v = this.view;
34782         setTimeout(function(){
34783             v.headersDisabled = false;
34784         }, 50);
34785     },
34786
34787     afterInvalidDrop : function(){
34788         var v = this.view;
34789         setTimeout(function(){
34790             v.headersDisabled = false;
34791         }, 50);
34792     }
34793 });
34794 /*
34795  * Based on:
34796  * Ext JS Library 1.1.1
34797  * Copyright(c) 2006-2007, Ext JS, LLC.
34798  *
34799  * Originally Released Under LGPL - original licence link has changed is not relivant.
34800  *
34801  * Fork - LGPL
34802  * <script type="text/javascript">
34803  */
34804 // private
34805 // This is a support class used internally by the Grid components
34806 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
34807     this.grid = grid;
34808     this.view = grid.getView();
34809     // split the proxies so they don't interfere with mouse events
34810     this.proxyTop = Roo.DomHelper.append(document.body, {
34811         cls:"col-move-top", html:"&#160;"
34812     }, true);
34813     this.proxyBottom = Roo.DomHelper.append(document.body, {
34814         cls:"col-move-bottom", html:"&#160;"
34815     }, true);
34816     this.proxyTop.hide = this.proxyBottom.hide = function(){
34817         this.setLeftTop(-100,-100);
34818         this.setStyle("visibility", "hidden");
34819     };
34820     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
34821     // temporarily disabled
34822     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
34823     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
34824 };
34825 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
34826     proxyOffsets : [-4, -9],
34827     fly: Roo.Element.fly,
34828
34829     getTargetFromEvent : function(e){
34830         var t = Roo.lib.Event.getTarget(e);
34831         var cindex = this.view.findCellIndex(t);
34832         if(cindex !== false){
34833             return this.view.getHeaderCell(cindex);
34834         }
34835         return null;
34836     },
34837
34838     nextVisible : function(h){
34839         var v = this.view, cm = this.grid.colModel;
34840         h = h.nextSibling;
34841         while(h){
34842             if(!cm.isHidden(v.getCellIndex(h))){
34843                 return h;
34844             }
34845             h = h.nextSibling;
34846         }
34847         return null;
34848     },
34849
34850     prevVisible : function(h){
34851         var v = this.view, cm = this.grid.colModel;
34852         h = h.prevSibling;
34853         while(h){
34854             if(!cm.isHidden(v.getCellIndex(h))){
34855                 return h;
34856             }
34857             h = h.prevSibling;
34858         }
34859         return null;
34860     },
34861
34862     positionIndicator : function(h, n, e){
34863         var x = Roo.lib.Event.getPageX(e);
34864         var r = Roo.lib.Dom.getRegion(n.firstChild);
34865         var px, pt, py = r.top + this.proxyOffsets[1];
34866         if((r.right - x) <= (r.right-r.left)/2){
34867             px = r.right+this.view.borderWidth;
34868             pt = "after";
34869         }else{
34870             px = r.left;
34871             pt = "before";
34872         }
34873         var oldIndex = this.view.getCellIndex(h);
34874         var newIndex = this.view.getCellIndex(n);
34875
34876         if(this.grid.colModel.isFixed(newIndex)){
34877             return false;
34878         }
34879
34880         var locked = this.grid.colModel.isLocked(newIndex);
34881
34882         if(pt == "after"){
34883             newIndex++;
34884         }
34885         if(oldIndex < newIndex){
34886             newIndex--;
34887         }
34888         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
34889             return false;
34890         }
34891         px +=  this.proxyOffsets[0];
34892         this.proxyTop.setLeftTop(px, py);
34893         this.proxyTop.show();
34894         if(!this.bottomOffset){
34895             this.bottomOffset = this.view.mainHd.getHeight();
34896         }
34897         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
34898         this.proxyBottom.show();
34899         return pt;
34900     },
34901
34902     onNodeEnter : function(n, dd, e, data){
34903         if(data.header != n){
34904             this.positionIndicator(data.header, n, e);
34905         }
34906     },
34907
34908     onNodeOver : function(n, dd, e, data){
34909         var result = false;
34910         if(data.header != n){
34911             result = this.positionIndicator(data.header, n, e);
34912         }
34913         if(!result){
34914             this.proxyTop.hide();
34915             this.proxyBottom.hide();
34916         }
34917         return result ? this.dropAllowed : this.dropNotAllowed;
34918     },
34919
34920     onNodeOut : function(n, dd, e, data){
34921         this.proxyTop.hide();
34922         this.proxyBottom.hide();
34923     },
34924
34925     onNodeDrop : function(n, dd, e, data){
34926         var h = data.header;
34927         if(h != n){
34928             var cm = this.grid.colModel;
34929             var x = Roo.lib.Event.getPageX(e);
34930             var r = Roo.lib.Dom.getRegion(n.firstChild);
34931             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
34932             var oldIndex = this.view.getCellIndex(h);
34933             var newIndex = this.view.getCellIndex(n);
34934             var locked = cm.isLocked(newIndex);
34935             if(pt == "after"){
34936                 newIndex++;
34937             }
34938             if(oldIndex < newIndex){
34939                 newIndex--;
34940             }
34941             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
34942                 return false;
34943             }
34944             cm.setLocked(oldIndex, locked, true);
34945             cm.moveColumn(oldIndex, newIndex);
34946             this.grid.fireEvent("columnmove", oldIndex, newIndex);
34947             return true;
34948         }
34949         return false;
34950     }
34951 });
34952 /*
34953  * Based on:
34954  * Ext JS Library 1.1.1
34955  * Copyright(c) 2006-2007, Ext JS, LLC.
34956  *
34957  * Originally Released Under LGPL - original licence link has changed is not relivant.
34958  *
34959  * Fork - LGPL
34960  * <script type="text/javascript">
34961  */
34962   
34963 /**
34964  * @class Roo.grid.GridView
34965  * @extends Roo.util.Observable
34966  *
34967  * @constructor
34968  * @param {Object} config
34969  */
34970 Roo.grid.GridView = function(config){
34971     Roo.grid.GridView.superclass.constructor.call(this);
34972     this.el = null;
34973
34974     Roo.apply(this, config);
34975 };
34976
34977 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
34978
34979     
34980     rowClass : "x-grid-row",
34981
34982     cellClass : "x-grid-col",
34983
34984     tdClass : "x-grid-td",
34985
34986     hdClass : "x-grid-hd",
34987
34988     splitClass : "x-grid-split",
34989
34990     sortClasses : ["sort-asc", "sort-desc"],
34991
34992     enableMoveAnim : false,
34993
34994     hlColor: "C3DAF9",
34995
34996     dh : Roo.DomHelper,
34997
34998     fly : Roo.Element.fly,
34999
35000     css : Roo.util.CSS,
35001
35002     borderWidth: 1,
35003
35004     splitOffset: 3,
35005
35006     scrollIncrement : 22,
35007
35008     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
35009
35010     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
35011
35012     bind : function(ds, cm){
35013         if(this.ds){
35014             this.ds.un("load", this.onLoad, this);
35015             this.ds.un("datachanged", this.onDataChange, this);
35016             this.ds.un("add", this.onAdd, this);
35017             this.ds.un("remove", this.onRemove, this);
35018             this.ds.un("update", this.onUpdate, this);
35019             this.ds.un("clear", this.onClear, this);
35020         }
35021         if(ds){
35022             ds.on("load", this.onLoad, this);
35023             ds.on("datachanged", this.onDataChange, this);
35024             ds.on("add", this.onAdd, this);
35025             ds.on("remove", this.onRemove, this);
35026             ds.on("update", this.onUpdate, this);
35027             ds.on("clear", this.onClear, this);
35028         }
35029         this.ds = ds;
35030
35031         if(this.cm){
35032             this.cm.un("widthchange", this.onColWidthChange, this);
35033             this.cm.un("headerchange", this.onHeaderChange, this);
35034             this.cm.un("hiddenchange", this.onHiddenChange, this);
35035             this.cm.un("columnmoved", this.onColumnMove, this);
35036             this.cm.un("columnlockchange", this.onColumnLock, this);
35037         }
35038         if(cm){
35039             this.generateRules(cm);
35040             cm.on("widthchange", this.onColWidthChange, this);
35041             cm.on("headerchange", this.onHeaderChange, this);
35042             cm.on("hiddenchange", this.onHiddenChange, this);
35043             cm.on("columnmoved", this.onColumnMove, this);
35044             cm.on("columnlockchange", this.onColumnLock, this);
35045         }
35046         this.cm = cm;
35047     },
35048
35049     init: function(grid){
35050         Roo.grid.GridView.superclass.init.call(this, grid);
35051
35052         this.bind(grid.dataSource, grid.colModel);
35053
35054         grid.on("headerclick", this.handleHeaderClick, this);
35055
35056         if(grid.trackMouseOver){
35057             grid.on("mouseover", this.onRowOver, this);
35058             grid.on("mouseout", this.onRowOut, this);
35059         }
35060         grid.cancelTextSelection = function(){};
35061         this.gridId = grid.id;
35062
35063         var tpls = this.templates || {};
35064
35065         if(!tpls.master){
35066             tpls.master = new Roo.Template(
35067                '<div class="x-grid" hidefocus="true">',
35068                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
35069                   '<div class="x-grid-topbar"></div>',
35070                   '<div class="x-grid-scroller"><div></div></div>',
35071                   '<div class="x-grid-locked">',
35072                       '<div class="x-grid-header">{lockedHeader}</div>',
35073                       '<div class="x-grid-body">{lockedBody}</div>',
35074                   "</div>",
35075                   '<div class="x-grid-viewport">',
35076                       '<div class="x-grid-header">{header}</div>',
35077                       '<div class="x-grid-body">{body}</div>',
35078                   "</div>",
35079                   '<div class="x-grid-bottombar"></div>',
35080                  
35081                   '<div class="x-grid-resize-proxy">&#160;</div>',
35082                "</div>"
35083             );
35084             tpls.master.disableformats = true;
35085         }
35086
35087         if(!tpls.header){
35088             tpls.header = new Roo.Template(
35089                '<table border="0" cellspacing="0" cellpadding="0">',
35090                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
35091                "</table>{splits}"
35092             );
35093             tpls.header.disableformats = true;
35094         }
35095         tpls.header.compile();
35096
35097         if(!tpls.hcell){
35098             tpls.hcell = new Roo.Template(
35099                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
35100                 '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
35101                 "</div></td>"
35102              );
35103              tpls.hcell.disableFormats = true;
35104         }
35105         tpls.hcell.compile();
35106
35107         if(!tpls.hsplit){
35108             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
35109             tpls.hsplit.disableFormats = true;
35110         }
35111         tpls.hsplit.compile();
35112
35113         if(!tpls.body){
35114             tpls.body = new Roo.Template(
35115                '<table border="0" cellspacing="0" cellpadding="0">',
35116                "<tbody>{rows}</tbody>",
35117                "</table>"
35118             );
35119             tpls.body.disableFormats = true;
35120         }
35121         tpls.body.compile();
35122
35123         if(!tpls.row){
35124             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
35125             tpls.row.disableFormats = true;
35126         }
35127         tpls.row.compile();
35128
35129         if(!tpls.cell){
35130             tpls.cell = new Roo.Template(
35131                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
35132                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
35133                 "</td>"
35134             );
35135             tpls.cell.disableFormats = true;
35136         }
35137         tpls.cell.compile();
35138
35139         this.templates = tpls;
35140     },
35141
35142     // remap these for backwards compat
35143     onColWidthChange : function(){
35144         this.updateColumns.apply(this, arguments);
35145     },
35146     onHeaderChange : function(){
35147         this.updateHeaders.apply(this, arguments);
35148     }, 
35149     onHiddenChange : function(){
35150         this.handleHiddenChange.apply(this, arguments);
35151     },
35152     onColumnMove : function(){
35153         this.handleColumnMove.apply(this, arguments);
35154     },
35155     onColumnLock : function(){
35156         this.handleLockChange.apply(this, arguments);
35157     },
35158
35159     onDataChange : function(){
35160         this.refresh();
35161         this.updateHeaderSortState();
35162     },
35163
35164     onClear : function(){
35165         this.refresh();
35166     },
35167
35168     onUpdate : function(ds, record){
35169         this.refreshRow(record);
35170     },
35171
35172     refreshRow : function(record){
35173         var ds = this.ds, index;
35174         if(typeof record == 'number'){
35175             index = record;
35176             record = ds.getAt(index);
35177         }else{
35178             index = ds.indexOf(record);
35179         }
35180         this.insertRows(ds, index, index, true);
35181         this.onRemove(ds, record, index+1, true);
35182         this.syncRowHeights(index, index);
35183         this.layout();
35184         this.fireEvent("rowupdated", this, index, record);
35185     },
35186
35187     onAdd : function(ds, records, index){
35188         this.insertRows(ds, index, index + (records.length-1));
35189     },
35190
35191     onRemove : function(ds, record, index, isUpdate){
35192         if(isUpdate !== true){
35193             this.fireEvent("beforerowremoved", this, index, record);
35194         }
35195         var bt = this.getBodyTable(), lt = this.getLockedTable();
35196         if(bt.rows[index]){
35197             bt.firstChild.removeChild(bt.rows[index]);
35198         }
35199         if(lt.rows[index]){
35200             lt.firstChild.removeChild(lt.rows[index]);
35201         }
35202         if(isUpdate !== true){
35203             this.stripeRows(index);
35204             this.syncRowHeights(index, index);
35205             this.layout();
35206             this.fireEvent("rowremoved", this, index, record);
35207         }
35208     },
35209
35210     onLoad : function(){
35211         this.scrollToTop();
35212     },
35213
35214     /**
35215      * Scrolls the grid to the top
35216      */
35217     scrollToTop : function(){
35218         if(this.scroller){
35219             this.scroller.dom.scrollTop = 0;
35220             this.syncScroll();
35221         }
35222     },
35223
35224     /**
35225      * Gets a panel in the header of the grid that can be used for toolbars etc.
35226      * After modifying the contents of this panel a call to grid.autoSize() may be
35227      * required to register any changes in size.
35228      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
35229      * @return Roo.Element
35230      */
35231     getHeaderPanel : function(doShow){
35232         if(doShow){
35233             this.headerPanel.show();
35234         }
35235         return this.headerPanel;
35236     },
35237
35238     /**
35239      * Gets a panel in the footer of the grid that can be used for toolbars etc.
35240      * After modifying the contents of this panel a call to grid.autoSize() may be
35241      * required to register any changes in size.
35242      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
35243      * @return Roo.Element
35244      */
35245     getFooterPanel : function(doShow){
35246         if(doShow){
35247             this.footerPanel.show();
35248         }
35249         return this.footerPanel;
35250     },
35251
35252     initElements : function(){
35253         var E = Roo.Element;
35254         var el = this.grid.getGridEl().dom.firstChild;
35255         var cs = el.childNodes;
35256
35257         this.el = new E(el);
35258         
35259          this.focusEl = new E(el.firstChild);
35260         this.focusEl.swallowEvent("click", true);
35261         
35262         this.headerPanel = new E(cs[1]);
35263         this.headerPanel.enableDisplayMode("block");
35264
35265         this.scroller = new E(cs[2]);
35266         this.scrollSizer = new E(this.scroller.dom.firstChild);
35267
35268         this.lockedWrap = new E(cs[3]);
35269         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
35270         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
35271
35272         this.mainWrap = new E(cs[4]);
35273         this.mainHd = new E(this.mainWrap.dom.firstChild);
35274         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
35275
35276         this.footerPanel = new E(cs[5]);
35277         this.footerPanel.enableDisplayMode("block");
35278
35279         this.resizeProxy = new E(cs[6]);
35280
35281         this.headerSelector = String.format(
35282            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
35283            this.lockedHd.id, this.mainHd.id
35284         );
35285
35286         this.splitterSelector = String.format(
35287            '#{0} div.x-grid-split, #{1} div.x-grid-split',
35288            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
35289         );
35290     },
35291     idToCssName : function(s)
35292     {
35293         return s.replace(/[^a-z0-9]+/ig, '-');
35294     },
35295
35296     getHeaderCell : function(index){
35297         return Roo.DomQuery.select(this.headerSelector)[index];
35298     },
35299
35300     getHeaderCellMeasure : function(index){
35301         return this.getHeaderCell(index).firstChild;
35302     },
35303
35304     getHeaderCellText : function(index){
35305         return this.getHeaderCell(index).firstChild.firstChild;
35306     },
35307
35308     getLockedTable : function(){
35309         return this.lockedBody.dom.firstChild;
35310     },
35311
35312     getBodyTable : function(){
35313         return this.mainBody.dom.firstChild;
35314     },
35315
35316     getLockedRow : function(index){
35317         return this.getLockedTable().rows[index];
35318     },
35319
35320     getRow : function(index){
35321         return this.getBodyTable().rows[index];
35322     },
35323
35324     getRowComposite : function(index){
35325         if(!this.rowEl){
35326             this.rowEl = new Roo.CompositeElementLite();
35327         }
35328         var els = [], lrow, mrow;
35329         if(lrow = this.getLockedRow(index)){
35330             els.push(lrow);
35331         }
35332         if(mrow = this.getRow(index)){
35333             els.push(mrow);
35334         }
35335         this.rowEl.elements = els;
35336         return this.rowEl;
35337     },
35338     /**
35339      * Gets the 'td' of the cell
35340      * 
35341      * @param {Integer} rowIndex row to select
35342      * @param {Integer} colIndex column to select
35343      * 
35344      * @return {Object} 
35345      */
35346     getCell : function(rowIndex, colIndex){
35347         var locked = this.cm.getLockedCount();
35348         var source;
35349         if(colIndex < locked){
35350             source = this.lockedBody.dom.firstChild;
35351         }else{
35352             source = this.mainBody.dom.firstChild;
35353             colIndex -= locked;
35354         }
35355         return source.rows[rowIndex].childNodes[colIndex];
35356     },
35357
35358     getCellText : function(rowIndex, colIndex){
35359         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
35360     },
35361
35362     getCellBox : function(cell){
35363         var b = this.fly(cell).getBox();
35364         if(Roo.isOpera){ // opera fails to report the Y
35365             b.y = cell.offsetTop + this.mainBody.getY();
35366         }
35367         return b;
35368     },
35369
35370     getCellIndex : function(cell){
35371         var id = String(cell.className).match(this.cellRE);
35372         if(id){
35373             return parseInt(id[1], 10);
35374         }
35375         return 0;
35376     },
35377
35378     findHeaderIndex : function(n){
35379         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
35380         return r ? this.getCellIndex(r) : false;
35381     },
35382
35383     findHeaderCell : function(n){
35384         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
35385         return r ? r : false;
35386     },
35387
35388     findRowIndex : function(n){
35389         if(!n){
35390             return false;
35391         }
35392         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
35393         return r ? r.rowIndex : false;
35394     },
35395
35396     findCellIndex : function(node){
35397         var stop = this.el.dom;
35398         while(node && node != stop){
35399             if(this.findRE.test(node.className)){
35400                 return this.getCellIndex(node);
35401             }
35402             node = node.parentNode;
35403         }
35404         return false;
35405     },
35406
35407     getColumnId : function(index){
35408         return this.cm.getColumnId(index);
35409     },
35410
35411     getSplitters : function()
35412     {
35413         if(this.splitterSelector){
35414            return Roo.DomQuery.select(this.splitterSelector);
35415         }else{
35416             return null;
35417       }
35418     },
35419
35420     getSplitter : function(index){
35421         return this.getSplitters()[index];
35422     },
35423
35424     onRowOver : function(e, t){
35425         var row;
35426         if((row = this.findRowIndex(t)) !== false){
35427             this.getRowComposite(row).addClass("x-grid-row-over");
35428         }
35429     },
35430
35431     onRowOut : function(e, t){
35432         var row;
35433         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
35434             this.getRowComposite(row).removeClass("x-grid-row-over");
35435         }
35436     },
35437
35438     renderHeaders : function(){
35439         var cm = this.cm;
35440         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
35441         var cb = [], lb = [], sb = [], lsb = [], p = {};
35442         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
35443             p.cellId = "x-grid-hd-0-" + i;
35444             p.splitId = "x-grid-csplit-0-" + i;
35445             p.id = cm.getColumnId(i);
35446             p.title = cm.getColumnTooltip(i) || "";
35447             p.value = cm.getColumnHeader(i) || "";
35448             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
35449             if(!cm.isLocked(i)){
35450                 cb[cb.length] = ct.apply(p);
35451                 sb[sb.length] = st.apply(p);
35452             }else{
35453                 lb[lb.length] = ct.apply(p);
35454                 lsb[lsb.length] = st.apply(p);
35455             }
35456         }
35457         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
35458                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
35459     },
35460
35461     updateHeaders : function(){
35462         var html = this.renderHeaders();
35463         this.lockedHd.update(html[0]);
35464         this.mainHd.update(html[1]);
35465     },
35466
35467     /**
35468      * Focuses the specified row.
35469      * @param {Number} row The row index
35470      */
35471     focusRow : function(row)
35472     {
35473         //Roo.log('GridView.focusRow');
35474         var x = this.scroller.dom.scrollLeft;
35475         this.focusCell(row, 0, false);
35476         this.scroller.dom.scrollLeft = x;
35477     },
35478
35479     /**
35480      * Focuses the specified cell.
35481      * @param {Number} row The row index
35482      * @param {Number} col The column index
35483      * @param {Boolean} hscroll false to disable horizontal scrolling
35484      */
35485     focusCell : function(row, col, hscroll)
35486     {
35487         //Roo.log('GridView.focusCell');
35488         var el = this.ensureVisible(row, col, hscroll);
35489         this.focusEl.alignTo(el, "tl-tl");
35490         if(Roo.isGecko){
35491             this.focusEl.focus();
35492         }else{
35493             this.focusEl.focus.defer(1, this.focusEl);
35494         }
35495     },
35496
35497     /**
35498      * Scrolls the specified cell into view
35499      * @param {Number} row The row index
35500      * @param {Number} col The column index
35501      * @param {Boolean} hscroll false to disable horizontal scrolling
35502      */
35503     ensureVisible : function(row, col, hscroll)
35504     {
35505         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
35506         //return null; //disable for testing.
35507         if(typeof row != "number"){
35508             row = row.rowIndex;
35509         }
35510         if(row < 0 && row >= this.ds.getCount()){
35511             return  null;
35512         }
35513         col = (col !== undefined ? col : 0);
35514         var cm = this.grid.colModel;
35515         while(cm.isHidden(col)){
35516             col++;
35517         }
35518
35519         var el = this.getCell(row, col);
35520         if(!el){
35521             return null;
35522         }
35523         var c = this.scroller.dom;
35524
35525         var ctop = parseInt(el.offsetTop, 10);
35526         var cleft = parseInt(el.offsetLeft, 10);
35527         var cbot = ctop + el.offsetHeight;
35528         var cright = cleft + el.offsetWidth;
35529         
35530         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
35531         var stop = parseInt(c.scrollTop, 10);
35532         var sleft = parseInt(c.scrollLeft, 10);
35533         var sbot = stop + ch;
35534         var sright = sleft + c.clientWidth;
35535         /*
35536         Roo.log('GridView.ensureVisible:' +
35537                 ' ctop:' + ctop +
35538                 ' c.clientHeight:' + c.clientHeight +
35539                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
35540                 ' stop:' + stop +
35541                 ' cbot:' + cbot +
35542                 ' sbot:' + sbot +
35543                 ' ch:' + ch  
35544                 );
35545         */
35546         if(ctop < stop){
35547              c.scrollTop = ctop;
35548             //Roo.log("set scrolltop to ctop DISABLE?");
35549         }else if(cbot > sbot){
35550             //Roo.log("set scrolltop to cbot-ch");
35551             c.scrollTop = cbot-ch;
35552         }
35553         
35554         if(hscroll !== false){
35555             if(cleft < sleft){
35556                 c.scrollLeft = cleft;
35557             }else if(cright > sright){
35558                 c.scrollLeft = cright-c.clientWidth;
35559             }
35560         }
35561          
35562         return el;
35563     },
35564
35565     updateColumns : function(){
35566         this.grid.stopEditing();
35567         var cm = this.grid.colModel, colIds = this.getColumnIds();
35568         //var totalWidth = cm.getTotalWidth();
35569         var pos = 0;
35570         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
35571             //if(cm.isHidden(i)) continue;
35572             var w = cm.getColumnWidth(i);
35573             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
35574             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
35575         }
35576         this.updateSplitters();
35577     },
35578
35579     generateRules : function(cm){
35580         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
35581         Roo.util.CSS.removeStyleSheet(rulesId);
35582         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
35583             var cid = cm.getColumnId(i);
35584             var align = '';
35585             if(cm.config[i].align){
35586                 align = 'text-align:'+cm.config[i].align+';';
35587             }
35588             var hidden = '';
35589             if(cm.isHidden(i)){
35590                 hidden = 'display:none;';
35591             }
35592             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
35593             ruleBuf.push(
35594                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
35595                     this.hdSelector, cid, " {\n", align, width, "}\n",
35596                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
35597                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
35598         }
35599         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
35600     },
35601
35602     updateSplitters : function(){
35603         var cm = this.cm, s = this.getSplitters();
35604         if(s){ // splitters not created yet
35605             var pos = 0, locked = true;
35606             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
35607                 if(cm.isHidden(i)) continue;
35608                 var w = cm.getColumnWidth(i); // make sure it's a number
35609                 if(!cm.isLocked(i) && locked){
35610                     pos = 0;
35611                     locked = false;
35612                 }
35613                 pos += w;
35614                 s[i].style.left = (pos-this.splitOffset) + "px";
35615             }
35616         }
35617     },
35618
35619     handleHiddenChange : function(colModel, colIndex, hidden){
35620         if(hidden){
35621             this.hideColumn(colIndex);
35622         }else{
35623             this.unhideColumn(colIndex);
35624         }
35625     },
35626
35627     hideColumn : function(colIndex){
35628         var cid = this.getColumnId(colIndex);
35629         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
35630         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
35631         if(Roo.isSafari){
35632             this.updateHeaders();
35633         }
35634         this.updateSplitters();
35635         this.layout();
35636     },
35637
35638     unhideColumn : function(colIndex){
35639         var cid = this.getColumnId(colIndex);
35640         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
35641         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
35642
35643         if(Roo.isSafari){
35644             this.updateHeaders();
35645         }
35646         this.updateSplitters();
35647         this.layout();
35648     },
35649
35650     insertRows : function(dm, firstRow, lastRow, isUpdate){
35651         if(firstRow == 0 && lastRow == dm.getCount()-1){
35652             this.refresh();
35653         }else{
35654             if(!isUpdate){
35655                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
35656             }
35657             var s = this.getScrollState();
35658             var markup = this.renderRows(firstRow, lastRow);
35659             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
35660             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
35661             this.restoreScroll(s);
35662             if(!isUpdate){
35663                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
35664                 this.syncRowHeights(firstRow, lastRow);
35665                 this.stripeRows(firstRow);
35666                 this.layout();
35667             }
35668         }
35669     },
35670
35671     bufferRows : function(markup, target, index){
35672         var before = null, trows = target.rows, tbody = target.tBodies[0];
35673         if(index < trows.length){
35674             before = trows[index];
35675         }
35676         var b = document.createElement("div");
35677         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
35678         var rows = b.firstChild.rows;
35679         for(var i = 0, len = rows.length; i < len; i++){
35680             if(before){
35681                 tbody.insertBefore(rows[0], before);
35682             }else{
35683                 tbody.appendChild(rows[0]);
35684             }
35685         }
35686         b.innerHTML = "";
35687         b = null;
35688     },
35689
35690     deleteRows : function(dm, firstRow, lastRow){
35691         if(dm.getRowCount()<1){
35692             this.fireEvent("beforerefresh", this);
35693             this.mainBody.update("");
35694             this.lockedBody.update("");
35695             this.fireEvent("refresh", this);
35696         }else{
35697             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
35698             var bt = this.getBodyTable();
35699             var tbody = bt.firstChild;
35700             var rows = bt.rows;
35701             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
35702                 tbody.removeChild(rows[firstRow]);
35703             }
35704             this.stripeRows(firstRow);
35705             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
35706         }
35707     },
35708
35709     updateRows : function(dataSource, firstRow, lastRow){
35710         var s = this.getScrollState();
35711         this.refresh();
35712         this.restoreScroll(s);
35713     },
35714
35715     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
35716         if(!noRefresh){
35717            this.refresh();
35718         }
35719         this.updateHeaderSortState();
35720     },
35721
35722     getScrollState : function(){
35723         
35724         var sb = this.scroller.dom;
35725         return {left: sb.scrollLeft, top: sb.scrollTop};
35726     },
35727
35728     stripeRows : function(startRow){
35729         if(!this.grid.stripeRows || this.ds.getCount() < 1){
35730             return;
35731         }
35732         startRow = startRow || 0;
35733         var rows = this.getBodyTable().rows;
35734         var lrows = this.getLockedTable().rows;
35735         var cls = ' x-grid-row-alt ';
35736         for(var i = startRow, len = rows.length; i < len; i++){
35737             var row = rows[i], lrow = lrows[i];
35738             var isAlt = ((i+1) % 2 == 0);
35739             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
35740             if(isAlt == hasAlt){
35741                 continue;
35742             }
35743             if(isAlt){
35744                 row.className += " x-grid-row-alt";
35745             }else{
35746                 row.className = row.className.replace("x-grid-row-alt", "");
35747             }
35748             if(lrow){
35749                 lrow.className = row.className;
35750             }
35751         }
35752     },
35753
35754     restoreScroll : function(state){
35755         //Roo.log('GridView.restoreScroll');
35756         var sb = this.scroller.dom;
35757         sb.scrollLeft = state.left;
35758         sb.scrollTop = state.top;
35759         this.syncScroll();
35760     },
35761
35762     syncScroll : function(){
35763         //Roo.log('GridView.syncScroll');
35764         var sb = this.scroller.dom;
35765         var sh = this.mainHd.dom;
35766         var bs = this.mainBody.dom;
35767         var lv = this.lockedBody.dom;
35768         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
35769         lv.scrollTop = bs.scrollTop = sb.scrollTop;
35770     },
35771
35772     handleScroll : function(e){
35773         this.syncScroll();
35774         var sb = this.scroller.dom;
35775         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
35776         e.stopEvent();
35777     },
35778
35779     handleWheel : function(e){
35780         var d = e.getWheelDelta();
35781         this.scroller.dom.scrollTop -= d*22;
35782         // set this here to prevent jumpy scrolling on large tables
35783         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
35784         e.stopEvent();
35785     },
35786
35787     renderRows : function(startRow, endRow){
35788         // pull in all the crap needed to render rows
35789         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
35790         var colCount = cm.getColumnCount();
35791
35792         if(ds.getCount() < 1){
35793             return ["", ""];
35794         }
35795
35796         // build a map for all the columns
35797         var cs = [];
35798         for(var i = 0; i < colCount; i++){
35799             var name = cm.getDataIndex(i);
35800             cs[i] = {
35801                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
35802                 renderer : cm.getRenderer(i),
35803                 id : cm.getColumnId(i),
35804                 locked : cm.isLocked(i)
35805             };
35806         }
35807
35808         startRow = startRow || 0;
35809         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
35810
35811         // records to render
35812         var rs = ds.getRange(startRow, endRow);
35813
35814         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
35815     },
35816
35817     // As much as I hate to duplicate code, this was branched because FireFox really hates
35818     // [].join("") on strings. The performance difference was substantial enough to
35819     // branch this function
35820     doRender : Roo.isGecko ?
35821             function(cs, rs, ds, startRow, colCount, stripe){
35822                 var ts = this.templates, ct = ts.cell, rt = ts.row;
35823                 // buffers
35824                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
35825                 
35826                 var hasListener = this.grid.hasListener('rowclass');
35827                 var rowcfg = {};
35828                 for(var j = 0, len = rs.length; j < len; j++){
35829                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
35830                     for(var i = 0; i < colCount; i++){
35831                         c = cs[i];
35832                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
35833                         p.id = c.id;
35834                         p.css = p.attr = "";
35835                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
35836                         if(p.value == undefined || p.value === "") p.value = "&#160;";
35837                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
35838                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
35839                         }
35840                         var markup = ct.apply(p);
35841                         if(!c.locked){
35842                             cb+= markup;
35843                         }else{
35844                             lcb+= markup;
35845                         }
35846                     }
35847                     var alt = [];
35848                     if(stripe && ((rowIndex+1) % 2 == 0)){
35849                         alt.push("x-grid-row-alt")
35850                     }
35851                     if(r.dirty){
35852                         alt.push(  " x-grid-dirty-row");
35853                     }
35854                     rp.cells = lcb;
35855                     if(this.getRowClass){
35856                         alt.push(this.getRowClass(r, rowIndex));
35857                     }
35858                     if (hasListener) {
35859                         rowcfg = {
35860                              
35861                             record: r,
35862                             rowIndex : rowIndex,
35863                             rowClass : ''
35864                         }
35865                         this.grid.fireEvent('rowclass', this, rowcfg);
35866                         alt.push(rowcfg.rowClass);
35867                     }
35868                     rp.alt = alt.join(" ");
35869                     lbuf+= rt.apply(rp);
35870                     rp.cells = cb;
35871                     buf+=  rt.apply(rp);
35872                 }
35873                 return [lbuf, buf];
35874             } :
35875             function(cs, rs, ds, startRow, colCount, stripe){
35876                 var ts = this.templates, ct = ts.cell, rt = ts.row;
35877                 // buffers
35878                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
35879                 var hasListener = this.grid.hasListener('rowclass');
35880  
35881                 var rowcfg = {};
35882                 for(var j = 0, len = rs.length; j < len; j++){
35883                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
35884                     for(var i = 0; i < colCount; i++){
35885                         c = cs[i];
35886                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
35887                         p.id = c.id;
35888                         p.css = p.attr = "";
35889                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
35890                         if(p.value == undefined || p.value === "") p.value = "&#160;";
35891                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
35892                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
35893                         }
35894                         
35895                         var markup = ct.apply(p);
35896                         if(!c.locked){
35897                             cb[cb.length] = markup;
35898                         }else{
35899                             lcb[lcb.length] = markup;
35900                         }
35901                     }
35902                     var alt = [];
35903                     if(stripe && ((rowIndex+1) % 2 == 0)){
35904                         alt.push( "x-grid-row-alt");
35905                     }
35906                     if(r.dirty){
35907                         alt.push(" x-grid-dirty-row");
35908                     }
35909                     rp.cells = lcb;
35910                     if(this.getRowClass){
35911                         alt.push( this.getRowClass(r, rowIndex));
35912                     }
35913                     if (hasListener) {
35914                         rowcfg = {
35915                              
35916                             record: r,
35917                             rowIndex : rowIndex,
35918                             rowClass : ''
35919                         }
35920                         this.grid.fireEvent('rowclass', this, rowcfg);
35921                         alt.push(rowcfg.rowClass);
35922                     }
35923                     rp.alt = alt.join(" ");
35924                     rp.cells = lcb.join("");
35925                     lbuf[lbuf.length] = rt.apply(rp);
35926                     rp.cells = cb.join("");
35927                     buf[buf.length] =  rt.apply(rp);
35928                 }
35929                 return [lbuf.join(""), buf.join("")];
35930             },
35931
35932     renderBody : function(){
35933         var markup = this.renderRows();
35934         var bt = this.templates.body;
35935         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
35936     },
35937
35938     /**
35939      * Refreshes the grid
35940      * @param {Boolean} headersToo
35941      */
35942     refresh : function(headersToo){
35943         this.fireEvent("beforerefresh", this);
35944         this.grid.stopEditing();
35945         var result = this.renderBody();
35946         this.lockedBody.update(result[0]);
35947         this.mainBody.update(result[1]);
35948         if(headersToo === true){
35949             this.updateHeaders();
35950             this.updateColumns();
35951             this.updateSplitters();
35952             this.updateHeaderSortState();
35953         }
35954         this.syncRowHeights();
35955         this.layout();
35956         this.fireEvent("refresh", this);
35957     },
35958
35959     handleColumnMove : function(cm, oldIndex, newIndex){
35960         this.indexMap = null;
35961         var s = this.getScrollState();
35962         this.refresh(true);
35963         this.restoreScroll(s);
35964         this.afterMove(newIndex);
35965     },
35966
35967     afterMove : function(colIndex){
35968         if(this.enableMoveAnim && Roo.enableFx){
35969             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
35970         }
35971         // if multisort - fix sortOrder, and reload..
35972         if (this.grid.dataSource.multiSort) {
35973             // the we can call sort again..
35974             var dm = this.grid.dataSource;
35975             var cm = this.grid.colModel;
35976             var so = [];
35977             for(var i = 0; i < cm.config.length; i++ ) {
35978                 
35979                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
35980                     continue; // dont' bother, it's not in sort list or being set.
35981                 }
35982                 
35983                 so.push(cm.config[i].dataIndex);
35984             };
35985             dm.sortOrder = so;
35986             dm.load(dm.lastOptions);
35987             
35988             
35989         }
35990         
35991     },
35992
35993     updateCell : function(dm, rowIndex, dataIndex){
35994         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
35995         if(typeof colIndex == "undefined"){ // not present in grid
35996             return;
35997         }
35998         var cm = this.grid.colModel;
35999         var cell = this.getCell(rowIndex, colIndex);
36000         var cellText = this.getCellText(rowIndex, colIndex);
36001
36002         var p = {
36003             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
36004             id : cm.getColumnId(colIndex),
36005             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
36006         };
36007         var renderer = cm.getRenderer(colIndex);
36008         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
36009         if(typeof val == "undefined" || val === "") val = "&#160;";
36010         cellText.innerHTML = val;
36011         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
36012         this.syncRowHeights(rowIndex, rowIndex);
36013     },
36014
36015     calcColumnWidth : function(colIndex, maxRowsToMeasure){
36016         var maxWidth = 0;
36017         if(this.grid.autoSizeHeaders){
36018             var h = this.getHeaderCellMeasure(colIndex);
36019             maxWidth = Math.max(maxWidth, h.scrollWidth);
36020         }
36021         var tb, index;
36022         if(this.cm.isLocked(colIndex)){
36023             tb = this.getLockedTable();
36024             index = colIndex;
36025         }else{
36026             tb = this.getBodyTable();
36027             index = colIndex - this.cm.getLockedCount();
36028         }
36029         if(tb && tb.rows){
36030             var rows = tb.rows;
36031             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
36032             for(var i = 0; i < stopIndex; i++){
36033                 var cell = rows[i].childNodes[index].firstChild;
36034                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
36035             }
36036         }
36037         return maxWidth + /*margin for error in IE*/ 5;
36038     },
36039     /**
36040      * Autofit a column to its content.
36041      * @param {Number} colIndex
36042      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
36043      */
36044      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
36045          if(this.cm.isHidden(colIndex)){
36046              return; // can't calc a hidden column
36047          }
36048         if(forceMinSize){
36049             var cid = this.cm.getColumnId(colIndex);
36050             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
36051            if(this.grid.autoSizeHeaders){
36052                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
36053            }
36054         }
36055         var newWidth = this.calcColumnWidth(colIndex);
36056         this.cm.setColumnWidth(colIndex,
36057             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
36058         if(!suppressEvent){
36059             this.grid.fireEvent("columnresize", colIndex, newWidth);
36060         }
36061     },
36062
36063     /**
36064      * Autofits all columns to their content and then expands to fit any extra space in the grid
36065      */
36066      autoSizeColumns : function(){
36067         var cm = this.grid.colModel;
36068         var colCount = cm.getColumnCount();
36069         for(var i = 0; i < colCount; i++){
36070             this.autoSizeColumn(i, true, true);
36071         }
36072         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
36073             this.fitColumns();
36074         }else{
36075             this.updateColumns();
36076             this.layout();
36077         }
36078     },
36079
36080     /**
36081      * Autofits all columns to the grid's width proportionate with their current size
36082      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
36083      */
36084     fitColumns : function(reserveScrollSpace){
36085         var cm = this.grid.colModel;
36086         var colCount = cm.getColumnCount();
36087         var cols = [];
36088         var width = 0;
36089         var i, w;
36090         for (i = 0; i < colCount; i++){
36091             if(!cm.isHidden(i) && !cm.isFixed(i)){
36092                 w = cm.getColumnWidth(i);
36093                 cols.push(i);
36094                 cols.push(w);
36095                 width += w;
36096             }
36097         }
36098         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
36099         if(reserveScrollSpace){
36100             avail -= 17;
36101         }
36102         var frac = (avail - cm.getTotalWidth())/width;
36103         while (cols.length){
36104             w = cols.pop();
36105             i = cols.pop();
36106             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
36107         }
36108         this.updateColumns();
36109         this.layout();
36110     },
36111
36112     onRowSelect : function(rowIndex){
36113         var row = this.getRowComposite(rowIndex);
36114         row.addClass("x-grid-row-selected");
36115     },
36116
36117     onRowDeselect : function(rowIndex){
36118         var row = this.getRowComposite(rowIndex);
36119         row.removeClass("x-grid-row-selected");
36120     },
36121
36122     onCellSelect : function(row, col){
36123         var cell = this.getCell(row, col);
36124         if(cell){
36125             Roo.fly(cell).addClass("x-grid-cell-selected");
36126         }
36127     },
36128
36129     onCellDeselect : function(row, col){
36130         var cell = this.getCell(row, col);
36131         if(cell){
36132             Roo.fly(cell).removeClass("x-grid-cell-selected");
36133         }
36134     },
36135
36136     updateHeaderSortState : function(){
36137         
36138         // sort state can be single { field: xxx, direction : yyy}
36139         // or   { xxx=>ASC , yyy : DESC ..... }
36140         
36141         var mstate = {};
36142         if (!this.ds.multiSort) { 
36143             var state = this.ds.getSortState();
36144             if(!state){
36145                 return;
36146             }
36147             mstate[state.field] = state.direction;
36148             // FIXME... - this is not used here.. but might be elsewhere..
36149             this.sortState = state;
36150             
36151         } else {
36152             mstate = this.ds.sortToggle;
36153         }
36154         //remove existing sort classes..
36155         
36156         var sc = this.sortClasses;
36157         var hds = this.el.select(this.headerSelector).removeClass(sc);
36158         
36159         for(var f in mstate) {
36160         
36161             var sortColumn = this.cm.findColumnIndex(f);
36162             
36163             if(sortColumn != -1){
36164                 var sortDir = mstate[f];        
36165                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
36166             }
36167         }
36168         
36169          
36170         
36171     },
36172
36173
36174     handleHeaderClick : function(g, index){
36175         if(this.headersDisabled){
36176             return;
36177         }
36178         var dm = g.dataSource, cm = g.colModel;
36179         if(!cm.isSortable(index)){
36180             return;
36181         }
36182         g.stopEditing();
36183         
36184         if (dm.multiSort) {
36185             // update the sortOrder
36186             var so = [];
36187             for(var i = 0; i < cm.config.length; i++ ) {
36188                 
36189                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
36190                     continue; // dont' bother, it's not in sort list or being set.
36191                 }
36192                 
36193                 so.push(cm.config[i].dataIndex);
36194             };
36195             dm.sortOrder = so;
36196         }
36197         
36198         
36199         dm.sort(cm.getDataIndex(index));
36200     },
36201
36202
36203     destroy : function(){
36204         if(this.colMenu){
36205             this.colMenu.removeAll();
36206             Roo.menu.MenuMgr.unregister(this.colMenu);
36207             this.colMenu.getEl().remove();
36208             delete this.colMenu;
36209         }
36210         if(this.hmenu){
36211             this.hmenu.removeAll();
36212             Roo.menu.MenuMgr.unregister(this.hmenu);
36213             this.hmenu.getEl().remove();
36214             delete this.hmenu;
36215         }
36216         if(this.grid.enableColumnMove){
36217             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
36218             if(dds){
36219                 for(var dd in dds){
36220                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
36221                         var elid = dds[dd].dragElId;
36222                         dds[dd].unreg();
36223                         Roo.get(elid).remove();
36224                     } else if(dds[dd].config.isTarget){
36225                         dds[dd].proxyTop.remove();
36226                         dds[dd].proxyBottom.remove();
36227                         dds[dd].unreg();
36228                     }
36229                     if(Roo.dd.DDM.locationCache[dd]){
36230                         delete Roo.dd.DDM.locationCache[dd];
36231                     }
36232                 }
36233                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
36234             }
36235         }
36236         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
36237         this.bind(null, null);
36238         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
36239     },
36240
36241     handleLockChange : function(){
36242         this.refresh(true);
36243     },
36244
36245     onDenyColumnLock : function(){
36246
36247     },
36248
36249     onDenyColumnHide : function(){
36250
36251     },
36252
36253     handleHdMenuClick : function(item){
36254         var index = this.hdCtxIndex;
36255         var cm = this.cm, ds = this.ds;
36256         switch(item.id){
36257             case "asc":
36258                 ds.sort(cm.getDataIndex(index), "ASC");
36259                 break;
36260             case "desc":
36261                 ds.sort(cm.getDataIndex(index), "DESC");
36262                 break;
36263             case "lock":
36264                 var lc = cm.getLockedCount();
36265                 if(cm.getColumnCount(true) <= lc+1){
36266                     this.onDenyColumnLock();
36267                     return;
36268                 }
36269                 if(lc != index){
36270                     cm.setLocked(index, true, true);
36271                     cm.moveColumn(index, lc);
36272                     this.grid.fireEvent("columnmove", index, lc);
36273                 }else{
36274                     cm.setLocked(index, true);
36275                 }
36276             break;
36277             case "unlock":
36278                 var lc = cm.getLockedCount();
36279                 if((lc-1) != index){
36280                     cm.setLocked(index, false, true);
36281                     cm.moveColumn(index, lc-1);
36282                     this.grid.fireEvent("columnmove", index, lc-1);
36283                 }else{
36284                     cm.setLocked(index, false);
36285                 }
36286             break;
36287             default:
36288                 index = cm.getIndexById(item.id.substr(4));
36289                 if(index != -1){
36290                     if(item.checked && cm.getColumnCount(true) <= 1){
36291                         this.onDenyColumnHide();
36292                         return false;
36293                     }
36294                     cm.setHidden(index, item.checked);
36295                 }
36296         }
36297         return true;
36298     },
36299
36300     beforeColMenuShow : function(){
36301         var cm = this.cm,  colCount = cm.getColumnCount();
36302         this.colMenu.removeAll();
36303         for(var i = 0; i < colCount; i++){
36304             this.colMenu.add(new Roo.menu.CheckItem({
36305                 id: "col-"+cm.getColumnId(i),
36306                 text: cm.getColumnHeader(i),
36307                 checked: !cm.isHidden(i),
36308                 hideOnClick:false
36309             }));
36310         }
36311     },
36312
36313     handleHdCtx : function(g, index, e){
36314         e.stopEvent();
36315         var hd = this.getHeaderCell(index);
36316         this.hdCtxIndex = index;
36317         var ms = this.hmenu.items, cm = this.cm;
36318         ms.get("asc").setDisabled(!cm.isSortable(index));
36319         ms.get("desc").setDisabled(!cm.isSortable(index));
36320         if(this.grid.enableColLock !== false){
36321             ms.get("lock").setDisabled(cm.isLocked(index));
36322             ms.get("unlock").setDisabled(!cm.isLocked(index));
36323         }
36324         this.hmenu.show(hd, "tl-bl");
36325     },
36326
36327     handleHdOver : function(e){
36328         var hd = this.findHeaderCell(e.getTarget());
36329         if(hd && !this.headersDisabled){
36330             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
36331                this.fly(hd).addClass("x-grid-hd-over");
36332             }
36333         }
36334     },
36335
36336     handleHdOut : function(e){
36337         var hd = this.findHeaderCell(e.getTarget());
36338         if(hd){
36339             this.fly(hd).removeClass("x-grid-hd-over");
36340         }
36341     },
36342
36343     handleSplitDblClick : function(e, t){
36344         var i = this.getCellIndex(t);
36345         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
36346             this.autoSizeColumn(i, true);
36347             this.layout();
36348         }
36349     },
36350
36351     render : function(){
36352
36353         var cm = this.cm;
36354         var colCount = cm.getColumnCount();
36355
36356         if(this.grid.monitorWindowResize === true){
36357             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
36358         }
36359         var header = this.renderHeaders();
36360         var body = this.templates.body.apply({rows:""});
36361         var html = this.templates.master.apply({
36362             lockedBody: body,
36363             body: body,
36364             lockedHeader: header[0],
36365             header: header[1]
36366         });
36367
36368         //this.updateColumns();
36369
36370         this.grid.getGridEl().dom.innerHTML = html;
36371
36372         this.initElements();
36373         
36374         // a kludge to fix the random scolling effect in webkit
36375         this.el.on("scroll", function() {
36376             this.el.dom.scrollTop=0; // hopefully not recursive..
36377         },this);
36378
36379         this.scroller.on("scroll", this.handleScroll, this);
36380         this.lockedBody.on("mousewheel", this.handleWheel, this);
36381         this.mainBody.on("mousewheel", this.handleWheel, this);
36382
36383         this.mainHd.on("mouseover", this.handleHdOver, this);
36384         this.mainHd.on("mouseout", this.handleHdOut, this);
36385         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
36386                 {delegate: "."+this.splitClass});
36387
36388         this.lockedHd.on("mouseover", this.handleHdOver, this);
36389         this.lockedHd.on("mouseout", this.handleHdOut, this);
36390         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
36391                 {delegate: "."+this.splitClass});
36392
36393         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
36394             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
36395         }
36396
36397         this.updateSplitters();
36398
36399         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
36400             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
36401             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
36402         }
36403
36404         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
36405             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
36406             this.hmenu.add(
36407                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
36408                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
36409             );
36410             if(this.grid.enableColLock !== false){
36411                 this.hmenu.add('-',
36412                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
36413                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
36414                 );
36415             }
36416             if(this.grid.enableColumnHide !== false){
36417
36418                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
36419                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
36420                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
36421
36422                 this.hmenu.add('-',
36423                     {id:"columns", text: this.columnsText, menu: this.colMenu}
36424                 );
36425             }
36426             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
36427
36428             this.grid.on("headercontextmenu", this.handleHdCtx, this);
36429         }
36430
36431         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
36432             this.dd = new Roo.grid.GridDragZone(this.grid, {
36433                 ddGroup : this.grid.ddGroup || 'GridDD'
36434             });
36435         }
36436
36437         /*
36438         for(var i = 0; i < colCount; i++){
36439             if(cm.isHidden(i)){
36440                 this.hideColumn(i);
36441             }
36442             if(cm.config[i].align){
36443                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
36444                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
36445             }
36446         }*/
36447         
36448         this.updateHeaderSortState();
36449
36450         this.beforeInitialResize();
36451         this.layout(true);
36452
36453         // two part rendering gives faster view to the user
36454         this.renderPhase2.defer(1, this);
36455     },
36456
36457     renderPhase2 : function(){
36458         // render the rows now
36459         this.refresh();
36460         if(this.grid.autoSizeColumns){
36461             this.autoSizeColumns();
36462         }
36463     },
36464
36465     beforeInitialResize : function(){
36466
36467     },
36468
36469     onColumnSplitterMoved : function(i, w){
36470         this.userResized = true;
36471         var cm = this.grid.colModel;
36472         cm.setColumnWidth(i, w, true);
36473         var cid = cm.getColumnId(i);
36474         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
36475         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
36476         this.updateSplitters();
36477         this.layout();
36478         this.grid.fireEvent("columnresize", i, w);
36479     },
36480
36481     syncRowHeights : function(startIndex, endIndex){
36482         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
36483             startIndex = startIndex || 0;
36484             var mrows = this.getBodyTable().rows;
36485             var lrows = this.getLockedTable().rows;
36486             var len = mrows.length-1;
36487             endIndex = Math.min(endIndex || len, len);
36488             for(var i = startIndex; i <= endIndex; i++){
36489                 var m = mrows[i], l = lrows[i];
36490                 var h = Math.max(m.offsetHeight, l.offsetHeight);
36491                 m.style.height = l.style.height = h + "px";
36492             }
36493         }
36494     },
36495
36496     layout : function(initialRender, is2ndPass){
36497         var g = this.grid;
36498         var auto = g.autoHeight;
36499         var scrollOffset = 16;
36500         var c = g.getGridEl(), cm = this.cm,
36501                 expandCol = g.autoExpandColumn,
36502                 gv = this;
36503         //c.beginMeasure();
36504
36505         if(!c.dom.offsetWidth){ // display:none?
36506             if(initialRender){
36507                 this.lockedWrap.show();
36508                 this.mainWrap.show();
36509             }
36510             return;
36511         }
36512
36513         var hasLock = this.cm.isLocked(0);
36514
36515         var tbh = this.headerPanel.getHeight();
36516         var bbh = this.footerPanel.getHeight();
36517
36518         if(auto){
36519             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
36520             var newHeight = ch + c.getBorderWidth("tb");
36521             if(g.maxHeight){
36522                 newHeight = Math.min(g.maxHeight, newHeight);
36523             }
36524             c.setHeight(newHeight);
36525         }
36526
36527         if(g.autoWidth){
36528             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
36529         }
36530
36531         var s = this.scroller;
36532
36533         var csize = c.getSize(true);
36534
36535         this.el.setSize(csize.width, csize.height);
36536
36537         this.headerPanel.setWidth(csize.width);
36538         this.footerPanel.setWidth(csize.width);
36539
36540         var hdHeight = this.mainHd.getHeight();
36541         var vw = csize.width;
36542         var vh = csize.height - (tbh + bbh);
36543
36544         s.setSize(vw, vh);
36545
36546         var bt = this.getBodyTable();
36547         var ltWidth = hasLock ?
36548                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
36549
36550         var scrollHeight = bt.offsetHeight;
36551         var scrollWidth = ltWidth + bt.offsetWidth;
36552         var vscroll = false, hscroll = false;
36553
36554         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
36555
36556         var lw = this.lockedWrap, mw = this.mainWrap;
36557         var lb = this.lockedBody, mb = this.mainBody;
36558
36559         setTimeout(function(){
36560             var t = s.dom.offsetTop;
36561             var w = s.dom.clientWidth,
36562                 h = s.dom.clientHeight;
36563
36564             lw.setTop(t);
36565             lw.setSize(ltWidth, h);
36566
36567             mw.setLeftTop(ltWidth, t);
36568             mw.setSize(w-ltWidth, h);
36569
36570             lb.setHeight(h-hdHeight);
36571             mb.setHeight(h-hdHeight);
36572
36573             if(is2ndPass !== true && !gv.userResized && expandCol){
36574                 // high speed resize without full column calculation
36575                 
36576                 var ci = cm.getIndexById(expandCol);
36577                 if (ci < 0) {
36578                     ci = cm.findColumnIndex(expandCol);
36579                 }
36580                 ci = Math.max(0, ci); // make sure it's got at least the first col.
36581                 var expandId = cm.getColumnId(ci);
36582                 var  tw = cm.getTotalWidth(false);
36583                 var currentWidth = cm.getColumnWidth(ci);
36584                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
36585                 if(currentWidth != cw){
36586                     cm.setColumnWidth(ci, cw, true);
36587                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
36588                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
36589                     gv.updateSplitters();
36590                     gv.layout(false, true);
36591                 }
36592             }
36593
36594             if(initialRender){
36595                 lw.show();
36596                 mw.show();
36597             }
36598             //c.endMeasure();
36599         }, 10);
36600     },
36601
36602     onWindowResize : function(){
36603         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
36604             return;
36605         }
36606         this.layout();
36607     },
36608
36609     appendFooter : function(parentEl){
36610         return null;
36611     },
36612
36613     sortAscText : "Sort Ascending",
36614     sortDescText : "Sort Descending",
36615     lockText : "Lock Column",
36616     unlockText : "Unlock Column",
36617     columnsText : "Columns"
36618 });
36619
36620
36621 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
36622     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
36623     this.proxy.el.addClass('x-grid3-col-dd');
36624 };
36625
36626 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
36627     handleMouseDown : function(e){
36628
36629     },
36630
36631     callHandleMouseDown : function(e){
36632         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
36633     }
36634 });
36635 /*
36636  * Based on:
36637  * Ext JS Library 1.1.1
36638  * Copyright(c) 2006-2007, Ext JS, LLC.
36639  *
36640  * Originally Released Under LGPL - original licence link has changed is not relivant.
36641  *
36642  * Fork - LGPL
36643  * <script type="text/javascript">
36644  */
36645  
36646 // private
36647 // This is a support class used internally by the Grid components
36648 Roo.grid.SplitDragZone = function(grid, hd, hd2){
36649     this.grid = grid;
36650     this.view = grid.getView();
36651     this.proxy = this.view.resizeProxy;
36652     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
36653         "gridSplitters" + this.grid.getGridEl().id, {
36654         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
36655     });
36656     this.setHandleElId(Roo.id(hd));
36657     this.setOuterHandleElId(Roo.id(hd2));
36658     this.scroll = false;
36659 };
36660 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
36661     fly: Roo.Element.fly,
36662
36663     b4StartDrag : function(x, y){
36664         this.view.headersDisabled = true;
36665         this.proxy.setHeight(this.view.mainWrap.getHeight());
36666         var w = this.cm.getColumnWidth(this.cellIndex);
36667         var minw = Math.max(w-this.grid.minColumnWidth, 0);
36668         this.resetConstraints();
36669         this.setXConstraint(minw, 1000);
36670         this.setYConstraint(0, 0);
36671         this.minX = x - minw;
36672         this.maxX = x + 1000;
36673         this.startPos = x;
36674         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
36675     },
36676
36677
36678     handleMouseDown : function(e){
36679         ev = Roo.EventObject.setEvent(e);
36680         var t = this.fly(ev.getTarget());
36681         if(t.hasClass("x-grid-split")){
36682             this.cellIndex = this.view.getCellIndex(t.dom);
36683             this.split = t.dom;
36684             this.cm = this.grid.colModel;
36685             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
36686                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
36687             }
36688         }
36689     },
36690
36691     endDrag : function(e){
36692         this.view.headersDisabled = false;
36693         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
36694         var diff = endX - this.startPos;
36695         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
36696     },
36697
36698     autoOffset : function(){
36699         this.setDelta(0,0);
36700     }
36701 });/*
36702  * Based on:
36703  * Ext JS Library 1.1.1
36704  * Copyright(c) 2006-2007, Ext JS, LLC.
36705  *
36706  * Originally Released Under LGPL - original licence link has changed is not relivant.
36707  *
36708  * Fork - LGPL
36709  * <script type="text/javascript">
36710  */
36711  
36712 // private
36713 // This is a support class used internally by the Grid components
36714 Roo.grid.GridDragZone = function(grid, config){
36715     this.view = grid.getView();
36716     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
36717     if(this.view.lockedBody){
36718         this.setHandleElId(Roo.id(this.view.mainBody.dom));
36719         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
36720     }
36721     this.scroll = false;
36722     this.grid = grid;
36723     this.ddel = document.createElement('div');
36724     this.ddel.className = 'x-grid-dd-wrap';
36725 };
36726
36727 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
36728     ddGroup : "GridDD",
36729
36730     getDragData : function(e){
36731         var t = Roo.lib.Event.getTarget(e);
36732         var rowIndex = this.view.findRowIndex(t);
36733         if(rowIndex !== false){
36734             var sm = this.grid.selModel;
36735             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
36736               //  sm.mouseDown(e, t);
36737             //}
36738             if (e.hasModifier()){
36739                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
36740             }
36741             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
36742         }
36743         return false;
36744     },
36745
36746     onInitDrag : function(e){
36747         var data = this.dragData;
36748         this.ddel.innerHTML = this.grid.getDragDropText();
36749         this.proxy.update(this.ddel);
36750         // fire start drag?
36751     },
36752
36753     afterRepair : function(){
36754         this.dragging = false;
36755     },
36756
36757     getRepairXY : function(e, data){
36758         return false;
36759     },
36760
36761     onEndDrag : function(data, e){
36762         // fire end drag?
36763     },
36764
36765     onValidDrop : function(dd, e, id){
36766         // fire drag drop?
36767         this.hideProxy();
36768     },
36769
36770     beforeInvalidDrop : function(e, id){
36771
36772     }
36773 });/*
36774  * Based on:
36775  * Ext JS Library 1.1.1
36776  * Copyright(c) 2006-2007, Ext JS, LLC.
36777  *
36778  * Originally Released Under LGPL - original licence link has changed is not relivant.
36779  *
36780  * Fork - LGPL
36781  * <script type="text/javascript">
36782  */
36783  
36784
36785 /**
36786  * @class Roo.grid.ColumnModel
36787  * @extends Roo.util.Observable
36788  * This is the default implementation of a ColumnModel used by the Grid. It defines
36789  * the columns in the grid.
36790  * <br>Usage:<br>
36791  <pre><code>
36792  var colModel = new Roo.grid.ColumnModel([
36793         {header: "Ticker", width: 60, sortable: true, locked: true},
36794         {header: "Company Name", width: 150, sortable: true},
36795         {header: "Market Cap.", width: 100, sortable: true},
36796         {header: "$ Sales", width: 100, sortable: true, renderer: money},
36797         {header: "Employees", width: 100, sortable: true, resizable: false}
36798  ]);
36799  </code></pre>
36800  * <p>
36801  
36802  * The config options listed for this class are options which may appear in each
36803  * individual column definition.
36804  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
36805  * @constructor
36806  * @param {Object} config An Array of column config objects. See this class's
36807  * config objects for details.
36808 */
36809 Roo.grid.ColumnModel = function(config){
36810         /**
36811      * The config passed into the constructor
36812      */
36813     this.config = config;
36814     this.lookup = {};
36815
36816     // if no id, create one
36817     // if the column does not have a dataIndex mapping,
36818     // map it to the order it is in the config
36819     for(var i = 0, len = config.length; i < len; i++){
36820         var c = config[i];
36821         if(typeof c.dataIndex == "undefined"){
36822             c.dataIndex = i;
36823         }
36824         if(typeof c.renderer == "string"){
36825             c.renderer = Roo.util.Format[c.renderer];
36826         }
36827         if(typeof c.id == "undefined"){
36828             c.id = Roo.id();
36829         }
36830         if(c.editor && c.editor.xtype){
36831             c.editor  = Roo.factory(c.editor, Roo.grid);
36832         }
36833         if(c.editor && c.editor.isFormField){
36834             c.editor = new Roo.grid.GridEditor(c.editor);
36835         }
36836         this.lookup[c.id] = c;
36837     }
36838
36839     /**
36840      * The width of columns which have no width specified (defaults to 100)
36841      * @type Number
36842      */
36843     this.defaultWidth = 100;
36844
36845     /**
36846      * Default sortable of columns which have no sortable specified (defaults to false)
36847      * @type Boolean
36848      */
36849     this.defaultSortable = false;
36850
36851     this.addEvents({
36852         /**
36853              * @event widthchange
36854              * Fires when the width of a column changes.
36855              * @param {ColumnModel} this
36856              * @param {Number} columnIndex The column index
36857              * @param {Number} newWidth The new width
36858              */
36859             "widthchange": true,
36860         /**
36861              * @event headerchange
36862              * Fires when the text of a header changes.
36863              * @param {ColumnModel} this
36864              * @param {Number} columnIndex The column index
36865              * @param {Number} newText The new header text
36866              */
36867             "headerchange": true,
36868         /**
36869              * @event hiddenchange
36870              * Fires when a column is hidden or "unhidden".
36871              * @param {ColumnModel} this
36872              * @param {Number} columnIndex The column index
36873              * @param {Boolean} hidden true if hidden, false otherwise
36874              */
36875             "hiddenchange": true,
36876             /**
36877          * @event columnmoved
36878          * Fires when a column is moved.
36879          * @param {ColumnModel} this
36880          * @param {Number} oldIndex
36881          * @param {Number} newIndex
36882          */
36883         "columnmoved" : true,
36884         /**
36885          * @event columlockchange
36886          * Fires when a column's locked state is changed
36887          * @param {ColumnModel} this
36888          * @param {Number} colIndex
36889          * @param {Boolean} locked true if locked
36890          */
36891         "columnlockchange" : true
36892     });
36893     Roo.grid.ColumnModel.superclass.constructor.call(this);
36894 };
36895 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
36896     /**
36897      * @cfg {String} header The header text to display in the Grid view.
36898      */
36899     /**
36900      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
36901      * {@link Roo.data.Record} definition from which to draw the column's value. If not
36902      * specified, the column's index is used as an index into the Record's data Array.
36903      */
36904     /**
36905      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
36906      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
36907      */
36908     /**
36909      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
36910      * Defaults to the value of the {@link #defaultSortable} property.
36911      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
36912      */
36913     /**
36914      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
36915      */
36916     /**
36917      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
36918      */
36919     /**
36920      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
36921      */
36922     /**
36923      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
36924      */
36925     /**
36926      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
36927      * given the cell's data value. See {@link #setRenderer}. If not specified, the
36928      * default renderer uses the raw data value.
36929      */
36930        /**
36931      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
36932      */
36933     /**
36934      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
36935      */
36936
36937     /**
36938      * Returns the id of the column at the specified index.
36939      * @param {Number} index The column index
36940      * @return {String} the id
36941      */
36942     getColumnId : function(index){
36943         return this.config[index].id;
36944     },
36945
36946     /**
36947      * Returns the column for a specified id.
36948      * @param {String} id The column id
36949      * @return {Object} the column
36950      */
36951     getColumnById : function(id){
36952         return this.lookup[id];
36953     },
36954
36955     
36956     /**
36957      * Returns the column for a specified dataIndex.
36958      * @param {String} dataIndex The column dataIndex
36959      * @return {Object|Boolean} the column or false if not found
36960      */
36961     getColumnByDataIndex: function(dataIndex){
36962         var index = this.findColumnIndex(dataIndex);
36963         return index > -1 ? this.config[index] : false;
36964     },
36965     
36966     /**
36967      * Returns the index for a specified column id.
36968      * @param {String} id The column id
36969      * @return {Number} the index, or -1 if not found
36970      */
36971     getIndexById : function(id){
36972         for(var i = 0, len = this.config.length; i < len; i++){
36973             if(this.config[i].id == id){
36974                 return i;
36975             }
36976         }
36977         return -1;
36978     },
36979     
36980     /**
36981      * Returns the index for a specified column dataIndex.
36982      * @param {String} dataIndex The column dataIndex
36983      * @return {Number} the index, or -1 if not found
36984      */
36985     
36986     findColumnIndex : function(dataIndex){
36987         for(var i = 0, len = this.config.length; i < len; i++){
36988             if(this.config[i].dataIndex == dataIndex){
36989                 return i;
36990             }
36991         }
36992         return -1;
36993     },
36994     
36995     
36996     moveColumn : function(oldIndex, newIndex){
36997         var c = this.config[oldIndex];
36998         this.config.splice(oldIndex, 1);
36999         this.config.splice(newIndex, 0, c);
37000         this.dataMap = null;
37001         this.fireEvent("columnmoved", this, oldIndex, newIndex);
37002     },
37003
37004     isLocked : function(colIndex){
37005         return this.config[colIndex].locked === true;
37006     },
37007
37008     setLocked : function(colIndex, value, suppressEvent){
37009         if(this.isLocked(colIndex) == value){
37010             return;
37011         }
37012         this.config[colIndex].locked = value;
37013         if(!suppressEvent){
37014             this.fireEvent("columnlockchange", this, colIndex, value);
37015         }
37016     },
37017
37018     getTotalLockedWidth : function(){
37019         var totalWidth = 0;
37020         for(var i = 0; i < this.config.length; i++){
37021             if(this.isLocked(i) && !this.isHidden(i)){
37022                 this.totalWidth += this.getColumnWidth(i);
37023             }
37024         }
37025         return totalWidth;
37026     },
37027
37028     getLockedCount : function(){
37029         for(var i = 0, len = this.config.length; i < len; i++){
37030             if(!this.isLocked(i)){
37031                 return i;
37032             }
37033         }
37034     },
37035
37036     /**
37037      * Returns the number of columns.
37038      * @return {Number}
37039      */
37040     getColumnCount : function(visibleOnly){
37041         if(visibleOnly === true){
37042             var c = 0;
37043             for(var i = 0, len = this.config.length; i < len; i++){
37044                 if(!this.isHidden(i)){
37045                     c++;
37046                 }
37047             }
37048             return c;
37049         }
37050         return this.config.length;
37051     },
37052
37053     /**
37054      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
37055      * @param {Function} fn
37056      * @param {Object} scope (optional)
37057      * @return {Array} result
37058      */
37059     getColumnsBy : function(fn, scope){
37060         var r = [];
37061         for(var i = 0, len = this.config.length; i < len; i++){
37062             var c = this.config[i];
37063             if(fn.call(scope||this, c, i) === true){
37064                 r[r.length] = c;
37065             }
37066         }
37067         return r;
37068     },
37069
37070     /**
37071      * Returns true if the specified column is sortable.
37072      * @param {Number} col The column index
37073      * @return {Boolean}
37074      */
37075     isSortable : function(col){
37076         if(typeof this.config[col].sortable == "undefined"){
37077             return this.defaultSortable;
37078         }
37079         return this.config[col].sortable;
37080     },
37081
37082     /**
37083      * Returns the rendering (formatting) function defined for the column.
37084      * @param {Number} col The column index.
37085      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
37086      */
37087     getRenderer : function(col){
37088         if(!this.config[col].renderer){
37089             return Roo.grid.ColumnModel.defaultRenderer;
37090         }
37091         return this.config[col].renderer;
37092     },
37093
37094     /**
37095      * Sets the rendering (formatting) function for a column.
37096      * @param {Number} col The column index
37097      * @param {Function} fn The function to use to process the cell's raw data
37098      * to return HTML markup for the grid view. The render function is called with
37099      * the following parameters:<ul>
37100      * <li>Data value.</li>
37101      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
37102      * <li>css A CSS style string to apply to the table cell.</li>
37103      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
37104      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
37105      * <li>Row index</li>
37106      * <li>Column index</li>
37107      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
37108      */
37109     setRenderer : function(col, fn){
37110         this.config[col].renderer = fn;
37111     },
37112
37113     /**
37114      * Returns the width for the specified column.
37115      * @param {Number} col The column index
37116      * @return {Number}
37117      */
37118     getColumnWidth : function(col){
37119         return this.config[col].width * 1 || this.defaultWidth;
37120     },
37121
37122     /**
37123      * Sets the width for a column.
37124      * @param {Number} col The column index
37125      * @param {Number} width The new width
37126      */
37127     setColumnWidth : function(col, width, suppressEvent){
37128         this.config[col].width = width;
37129         this.totalWidth = null;
37130         if(!suppressEvent){
37131              this.fireEvent("widthchange", this, col, width);
37132         }
37133     },
37134
37135     /**
37136      * Returns the total width of all columns.
37137      * @param {Boolean} includeHidden True to include hidden column widths
37138      * @return {Number}
37139      */
37140     getTotalWidth : function(includeHidden){
37141         if(!this.totalWidth){
37142             this.totalWidth = 0;
37143             for(var i = 0, len = this.config.length; i < len; i++){
37144                 if(includeHidden || !this.isHidden(i)){
37145                     this.totalWidth += this.getColumnWidth(i);
37146                 }
37147             }
37148         }
37149         return this.totalWidth;
37150     },
37151
37152     /**
37153      * Returns the header for the specified column.
37154      * @param {Number} col The column index
37155      * @return {String}
37156      */
37157     getColumnHeader : function(col){
37158         return this.config[col].header;
37159     },
37160
37161     /**
37162      * Sets the header for a column.
37163      * @param {Number} col The column index
37164      * @param {String} header The new header
37165      */
37166     setColumnHeader : function(col, header){
37167         this.config[col].header = header;
37168         this.fireEvent("headerchange", this, col, header);
37169     },
37170
37171     /**
37172      * Returns the tooltip for the specified column.
37173      * @param {Number} col The column index
37174      * @return {String}
37175      */
37176     getColumnTooltip : function(col){
37177             return this.config[col].tooltip;
37178     },
37179     /**
37180      * Sets the tooltip for a column.
37181      * @param {Number} col The column index
37182      * @param {String} tooltip The new tooltip
37183      */
37184     setColumnTooltip : function(col, tooltip){
37185             this.config[col].tooltip = tooltip;
37186     },
37187
37188     /**
37189      * Returns the dataIndex for the specified column.
37190      * @param {Number} col The column index
37191      * @return {Number}
37192      */
37193     getDataIndex : function(col){
37194         return this.config[col].dataIndex;
37195     },
37196
37197     /**
37198      * Sets the dataIndex for a column.
37199      * @param {Number} col The column index
37200      * @param {Number} dataIndex The new dataIndex
37201      */
37202     setDataIndex : function(col, dataIndex){
37203         this.config[col].dataIndex = dataIndex;
37204     },
37205
37206     
37207     
37208     /**
37209      * Returns true if the cell is editable.
37210      * @param {Number} colIndex The column index
37211      * @param {Number} rowIndex The row index
37212      * @return {Boolean}
37213      */
37214     isCellEditable : function(colIndex, rowIndex){
37215         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
37216     },
37217
37218     /**
37219      * Returns the editor defined for the cell/column.
37220      * return false or null to disable editing.
37221      * @param {Number} colIndex The column index
37222      * @param {Number} rowIndex The row index
37223      * @return {Object}
37224      */
37225     getCellEditor : function(colIndex, rowIndex){
37226         return this.config[colIndex].editor;
37227     },
37228
37229     /**
37230      * Sets if a column is editable.
37231      * @param {Number} col The column index
37232      * @param {Boolean} editable True if the column is editable
37233      */
37234     setEditable : function(col, editable){
37235         this.config[col].editable = editable;
37236     },
37237
37238
37239     /**
37240      * Returns true if the column is hidden.
37241      * @param {Number} colIndex The column index
37242      * @return {Boolean}
37243      */
37244     isHidden : function(colIndex){
37245         return this.config[colIndex].hidden;
37246     },
37247
37248
37249     /**
37250      * Returns true if the column width cannot be changed
37251      */
37252     isFixed : function(colIndex){
37253         return this.config[colIndex].fixed;
37254     },
37255
37256     /**
37257      * Returns true if the column can be resized
37258      * @return {Boolean}
37259      */
37260     isResizable : function(colIndex){
37261         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
37262     },
37263     /**
37264      * Sets if a column is hidden.
37265      * @param {Number} colIndex The column index
37266      * @param {Boolean} hidden True if the column is hidden
37267      */
37268     setHidden : function(colIndex, hidden){
37269         this.config[colIndex].hidden = hidden;
37270         this.totalWidth = null;
37271         this.fireEvent("hiddenchange", this, colIndex, hidden);
37272     },
37273
37274     /**
37275      * Sets the editor for a column.
37276      * @param {Number} col The column index
37277      * @param {Object} editor The editor object
37278      */
37279     setEditor : function(col, editor){
37280         this.config[col].editor = editor;
37281     }
37282 });
37283
37284 Roo.grid.ColumnModel.defaultRenderer = function(value){
37285         if(typeof value == "string" && value.length < 1){
37286             return "&#160;";
37287         }
37288         return value;
37289 };
37290
37291 // Alias for backwards compatibility
37292 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
37293 /*
37294  * Based on:
37295  * Ext JS Library 1.1.1
37296  * Copyright(c) 2006-2007, Ext JS, LLC.
37297  *
37298  * Originally Released Under LGPL - original licence link has changed is not relivant.
37299  *
37300  * Fork - LGPL
37301  * <script type="text/javascript">
37302  */
37303
37304 /**
37305  * @class Roo.grid.AbstractSelectionModel
37306  * @extends Roo.util.Observable
37307  * Abstract base class for grid SelectionModels.  It provides the interface that should be
37308  * implemented by descendant classes.  This class should not be directly instantiated.
37309  * @constructor
37310  */
37311 Roo.grid.AbstractSelectionModel = function(){
37312     this.locked = false;
37313     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
37314 };
37315
37316 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
37317     /** @ignore Called by the grid automatically. Do not call directly. */
37318     init : function(grid){
37319         this.grid = grid;
37320         this.initEvents();
37321     },
37322
37323     /**
37324      * Locks the selections.
37325      */
37326     lock : function(){
37327         this.locked = true;
37328     },
37329
37330     /**
37331      * Unlocks the selections.
37332      */
37333     unlock : function(){
37334         this.locked = false;
37335     },
37336
37337     /**
37338      * Returns true if the selections are locked.
37339      * @return {Boolean}
37340      */
37341     isLocked : function(){
37342         return this.locked;
37343     }
37344 });/*
37345  * Based on:
37346  * Ext JS Library 1.1.1
37347  * Copyright(c) 2006-2007, Ext JS, LLC.
37348  *
37349  * Originally Released Under LGPL - original licence link has changed is not relivant.
37350  *
37351  * Fork - LGPL
37352  * <script type="text/javascript">
37353  */
37354 /**
37355  * @extends Roo.grid.AbstractSelectionModel
37356  * @class Roo.grid.RowSelectionModel
37357  * The default SelectionModel used by {@link Roo.grid.Grid}.
37358  * It supports multiple selections and keyboard selection/navigation. 
37359  * @constructor
37360  * @param {Object} config
37361  */
37362 Roo.grid.RowSelectionModel = function(config){
37363     Roo.apply(this, config);
37364     this.selections = new Roo.util.MixedCollection(false, function(o){
37365         return o.id;
37366     });
37367
37368     this.last = false;
37369     this.lastActive = false;
37370
37371     this.addEvents({
37372         /**
37373              * @event selectionchange
37374              * Fires when the selection changes
37375              * @param {SelectionModel} this
37376              */
37377             "selectionchange" : true,
37378         /**
37379              * @event afterselectionchange
37380              * Fires after the selection changes (eg. by key press or clicking)
37381              * @param {SelectionModel} this
37382              */
37383             "afterselectionchange" : true,
37384         /**
37385              * @event beforerowselect
37386              * Fires when a row is selected being selected, return false to cancel.
37387              * @param {SelectionModel} this
37388              * @param {Number} rowIndex The selected index
37389              * @param {Boolean} keepExisting False if other selections will be cleared
37390              */
37391             "beforerowselect" : true,
37392         /**
37393              * @event rowselect
37394              * Fires when a row is selected.
37395              * @param {SelectionModel} this
37396              * @param {Number} rowIndex The selected index
37397              * @param {Roo.data.Record} r The record
37398              */
37399             "rowselect" : true,
37400         /**
37401              * @event rowdeselect
37402              * Fires when a row is deselected.
37403              * @param {SelectionModel} this
37404              * @param {Number} rowIndex The selected index
37405              */
37406         "rowdeselect" : true
37407     });
37408     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
37409     this.locked = false;
37410 };
37411
37412 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
37413     /**
37414      * @cfg {Boolean} singleSelect
37415      * True to allow selection of only one row at a time (defaults to false)
37416      */
37417     singleSelect : false,
37418
37419     // private
37420     initEvents : function(){
37421
37422         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
37423             this.grid.on("mousedown", this.handleMouseDown, this);
37424         }else{ // allow click to work like normal
37425             this.grid.on("rowclick", this.handleDragableRowClick, this);
37426         }
37427
37428         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
37429             "up" : function(e){
37430                 if(!e.shiftKey){
37431                     this.selectPrevious(e.shiftKey);
37432                 }else if(this.last !== false && this.lastActive !== false){
37433                     var last = this.last;
37434                     this.selectRange(this.last,  this.lastActive-1);
37435                     this.grid.getView().focusRow(this.lastActive);
37436                     if(last !== false){
37437                         this.last = last;
37438                     }
37439                 }else{
37440                     this.selectFirstRow();
37441                 }
37442                 this.fireEvent("afterselectionchange", this);
37443             },
37444             "down" : function(e){
37445                 if(!e.shiftKey){
37446                     this.selectNext(e.shiftKey);
37447                 }else if(this.last !== false && this.lastActive !== false){
37448                     var last = this.last;
37449                     this.selectRange(this.last,  this.lastActive+1);
37450                     this.grid.getView().focusRow(this.lastActive);
37451                     if(last !== false){
37452                         this.last = last;
37453                     }
37454                 }else{
37455                     this.selectFirstRow();
37456                 }
37457                 this.fireEvent("afterselectionchange", this);
37458             },
37459             scope: this
37460         });
37461
37462         var view = this.grid.view;
37463         view.on("refresh", this.onRefresh, this);
37464         view.on("rowupdated", this.onRowUpdated, this);
37465         view.on("rowremoved", this.onRemove, this);
37466     },
37467
37468     // private
37469     onRefresh : function(){
37470         var ds = this.grid.dataSource, i, v = this.grid.view;
37471         var s = this.selections;
37472         s.each(function(r){
37473             if((i = ds.indexOfId(r.id)) != -1){
37474                 v.onRowSelect(i);
37475             }else{
37476                 s.remove(r);
37477             }
37478         });
37479     },
37480
37481     // private
37482     onRemove : function(v, index, r){
37483         this.selections.remove(r);
37484     },
37485
37486     // private
37487     onRowUpdated : function(v, index, r){
37488         if(this.isSelected(r)){
37489             v.onRowSelect(index);
37490         }
37491     },
37492
37493     /**
37494      * Select records.
37495      * @param {Array} records The records to select
37496      * @param {Boolean} keepExisting (optional) True to keep existing selections
37497      */
37498     selectRecords : function(records, keepExisting){
37499         if(!keepExisting){
37500             this.clearSelections();
37501         }
37502         var ds = this.grid.dataSource;
37503         for(var i = 0, len = records.length; i < len; i++){
37504             this.selectRow(ds.indexOf(records[i]), true);
37505         }
37506     },
37507
37508     /**
37509      * Gets the number of selected rows.
37510      * @return {Number}
37511      */
37512     getCount : function(){
37513         return this.selections.length;
37514     },
37515
37516     /**
37517      * Selects the first row in the grid.
37518      */
37519     selectFirstRow : function(){
37520         this.selectRow(0);
37521     },
37522
37523     /**
37524      * Select the last row.
37525      * @param {Boolean} keepExisting (optional) True to keep existing selections
37526      */
37527     selectLastRow : function(keepExisting){
37528         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
37529     },
37530
37531     /**
37532      * Selects the row immediately following the last selected row.
37533      * @param {Boolean} keepExisting (optional) True to keep existing selections
37534      */
37535     selectNext : function(keepExisting){
37536         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
37537             this.selectRow(this.last+1, keepExisting);
37538             this.grid.getView().focusRow(this.last);
37539         }
37540     },
37541
37542     /**
37543      * Selects the row that precedes the last selected row.
37544      * @param {Boolean} keepExisting (optional) True to keep existing selections
37545      */
37546     selectPrevious : function(keepExisting){
37547         if(this.last){
37548             this.selectRow(this.last-1, keepExisting);
37549             this.grid.getView().focusRow(this.last);
37550         }
37551     },
37552
37553     /**
37554      * Returns the selected records
37555      * @return {Array} Array of selected records
37556      */
37557     getSelections : function(){
37558         return [].concat(this.selections.items);
37559     },
37560
37561     /**
37562      * Returns the first selected record.
37563      * @return {Record}
37564      */
37565     getSelected : function(){
37566         return this.selections.itemAt(0);
37567     },
37568
37569
37570     /**
37571      * Clears all selections.
37572      */
37573     clearSelections : function(fast){
37574         if(this.locked) return;
37575         if(fast !== true){
37576             var ds = this.grid.dataSource;
37577             var s = this.selections;
37578             s.each(function(r){
37579                 this.deselectRow(ds.indexOfId(r.id));
37580             }, this);
37581             s.clear();
37582         }else{
37583             this.selections.clear();
37584         }
37585         this.last = false;
37586     },
37587
37588
37589     /**
37590      * Selects all rows.
37591      */
37592     selectAll : function(){
37593         if(this.locked) return;
37594         this.selections.clear();
37595         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
37596             this.selectRow(i, true);
37597         }
37598     },
37599
37600     /**
37601      * Returns True if there is a selection.
37602      * @return {Boolean}
37603      */
37604     hasSelection : function(){
37605         return this.selections.length > 0;
37606     },
37607
37608     /**
37609      * Returns True if the specified row is selected.
37610      * @param {Number/Record} record The record or index of the record to check
37611      * @return {Boolean}
37612      */
37613     isSelected : function(index){
37614         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
37615         return (r && this.selections.key(r.id) ? true : false);
37616     },
37617
37618     /**
37619      * Returns True if the specified record id is selected.
37620      * @param {String} id The id of record to check
37621      * @return {Boolean}
37622      */
37623     isIdSelected : function(id){
37624         return (this.selections.key(id) ? true : false);
37625     },
37626
37627     // private
37628     handleMouseDown : function(e, t){
37629         var view = this.grid.getView(), rowIndex;
37630         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
37631             return;
37632         };
37633         if(e.shiftKey && this.last !== false){
37634             var last = this.last;
37635             this.selectRange(last, rowIndex, e.ctrlKey);
37636             this.last = last; // reset the last
37637             view.focusRow(rowIndex);
37638         }else{
37639             var isSelected = this.isSelected(rowIndex);
37640             if(e.button !== 0 && isSelected){
37641                 view.focusRow(rowIndex);
37642             }else if(e.ctrlKey && isSelected){
37643                 this.deselectRow(rowIndex);
37644             }else if(!isSelected){
37645                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
37646                 view.focusRow(rowIndex);
37647             }
37648         }
37649         this.fireEvent("afterselectionchange", this);
37650     },
37651     // private
37652     handleDragableRowClick :  function(grid, rowIndex, e) 
37653     {
37654         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
37655             this.selectRow(rowIndex, false);
37656             grid.view.focusRow(rowIndex);
37657              this.fireEvent("afterselectionchange", this);
37658         }
37659     },
37660     
37661     /**
37662      * Selects multiple rows.
37663      * @param {Array} rows Array of the indexes of the row to select
37664      * @param {Boolean} keepExisting (optional) True to keep existing selections
37665      */
37666     selectRows : function(rows, keepExisting){
37667         if(!keepExisting){
37668             this.clearSelections();
37669         }
37670         for(var i = 0, len = rows.length; i < len; i++){
37671             this.selectRow(rows[i], true);
37672         }
37673     },
37674
37675     /**
37676      * Selects a range of rows. All rows in between startRow and endRow are also selected.
37677      * @param {Number} startRow The index of the first row in the range
37678      * @param {Number} endRow The index of the last row in the range
37679      * @param {Boolean} keepExisting (optional) True to retain existing selections
37680      */
37681     selectRange : function(startRow, endRow, keepExisting){
37682         if(this.locked) return;
37683         if(!keepExisting){
37684             this.clearSelections();
37685         }
37686         if(startRow <= endRow){
37687             for(var i = startRow; i <= endRow; i++){
37688                 this.selectRow(i, true);
37689             }
37690         }else{
37691             for(var i = startRow; i >= endRow; i--){
37692                 this.selectRow(i, true);
37693             }
37694         }
37695     },
37696
37697     /**
37698      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
37699      * @param {Number} startRow The index of the first row in the range
37700      * @param {Number} endRow The index of the last row in the range
37701      */
37702     deselectRange : function(startRow, endRow, preventViewNotify){
37703         if(this.locked) return;
37704         for(var i = startRow; i <= endRow; i++){
37705             this.deselectRow(i, preventViewNotify);
37706         }
37707     },
37708
37709     /**
37710      * Selects a row.
37711      * @param {Number} row The index of the row to select
37712      * @param {Boolean} keepExisting (optional) True to keep existing selections
37713      */
37714     selectRow : function(index, keepExisting, preventViewNotify){
37715         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
37716         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
37717             if(!keepExisting || this.singleSelect){
37718                 this.clearSelections();
37719             }
37720             var r = this.grid.dataSource.getAt(index);
37721             this.selections.add(r);
37722             this.last = this.lastActive = index;
37723             if(!preventViewNotify){
37724                 this.grid.getView().onRowSelect(index);
37725             }
37726             this.fireEvent("rowselect", this, index, r);
37727             this.fireEvent("selectionchange", this);
37728         }
37729     },
37730
37731     /**
37732      * Deselects a row.
37733      * @param {Number} row The index of the row to deselect
37734      */
37735     deselectRow : function(index, preventViewNotify){
37736         if(this.locked) return;
37737         if(this.last == index){
37738             this.last = false;
37739         }
37740         if(this.lastActive == index){
37741             this.lastActive = false;
37742         }
37743         var r = this.grid.dataSource.getAt(index);
37744         this.selections.remove(r);
37745         if(!preventViewNotify){
37746             this.grid.getView().onRowDeselect(index);
37747         }
37748         this.fireEvent("rowdeselect", this, index);
37749         this.fireEvent("selectionchange", this);
37750     },
37751
37752     // private
37753     restoreLast : function(){
37754         if(this._last){
37755             this.last = this._last;
37756         }
37757     },
37758
37759     // private
37760     acceptsNav : function(row, col, cm){
37761         return !cm.isHidden(col) && cm.isCellEditable(col, row);
37762     },
37763
37764     // private
37765     onEditorKey : function(field, e){
37766         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
37767         if(k == e.TAB){
37768             e.stopEvent();
37769             ed.completeEdit();
37770             if(e.shiftKey){
37771                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
37772             }else{
37773                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
37774             }
37775         }else if(k == e.ENTER && !e.ctrlKey){
37776             e.stopEvent();
37777             ed.completeEdit();
37778             if(e.shiftKey){
37779                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
37780             }else{
37781                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
37782             }
37783         }else if(k == e.ESC){
37784             ed.cancelEdit();
37785         }
37786         if(newCell){
37787             g.startEditing(newCell[0], newCell[1]);
37788         }
37789     }
37790 });/*
37791  * Based on:
37792  * Ext JS Library 1.1.1
37793  * Copyright(c) 2006-2007, Ext JS, LLC.
37794  *
37795  * Originally Released Under LGPL - original licence link has changed is not relivant.
37796  *
37797  * Fork - LGPL
37798  * <script type="text/javascript">
37799  */
37800 /**
37801  * @class Roo.grid.CellSelectionModel
37802  * @extends Roo.grid.AbstractSelectionModel
37803  * This class provides the basic implementation for cell selection in a grid.
37804  * @constructor
37805  * @param {Object} config The object containing the configuration of this model.
37806  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
37807  */
37808 Roo.grid.CellSelectionModel = function(config){
37809     Roo.apply(this, config);
37810
37811     this.selection = null;
37812
37813     this.addEvents({
37814         /**
37815              * @event beforerowselect
37816              * Fires before a cell is selected.
37817              * @param {SelectionModel} this
37818              * @param {Number} rowIndex The selected row index
37819              * @param {Number} colIndex The selected cell index
37820              */
37821             "beforecellselect" : true,
37822         /**
37823              * @event cellselect
37824              * Fires when a cell is selected.
37825              * @param {SelectionModel} this
37826              * @param {Number} rowIndex The selected row index
37827              * @param {Number} colIndex The selected cell index
37828              */
37829             "cellselect" : true,
37830         /**
37831              * @event selectionchange
37832              * Fires when the active selection changes.
37833              * @param {SelectionModel} this
37834              * @param {Object} selection null for no selection or an object (o) with two properties
37835                 <ul>
37836                 <li>o.record: the record object for the row the selection is in</li>
37837                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
37838                 </ul>
37839              */
37840             "selectionchange" : true,
37841         /**
37842              * @event tabend
37843              * Fires when the tab (or enter) was pressed on the last editable cell
37844              * You can use this to trigger add new row.
37845              * @param {SelectionModel} this
37846              */
37847             "tabend" : true,
37848          /**
37849              * @event beforeeditnext
37850              * Fires before the next editable sell is made active
37851              * You can use this to skip to another cell or fire the tabend
37852              *    if you set cell to false
37853              * @param {Object} eventdata object : { cell : [ row, col ] } 
37854              */
37855             "beforeeditnext" : true
37856     });
37857     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
37858 };
37859
37860 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
37861     
37862     enter_is_tab: false,
37863
37864     /** @ignore */
37865     initEvents : function(){
37866         this.grid.on("mousedown", this.handleMouseDown, this);
37867         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
37868         var view = this.grid.view;
37869         view.on("refresh", this.onViewChange, this);
37870         view.on("rowupdated", this.onRowUpdated, this);
37871         view.on("beforerowremoved", this.clearSelections, this);
37872         view.on("beforerowsinserted", this.clearSelections, this);
37873         if(this.grid.isEditor){
37874             this.grid.on("beforeedit", this.beforeEdit,  this);
37875         }
37876     },
37877
37878         //private
37879     beforeEdit : function(e){
37880         this.select(e.row, e.column, false, true, e.record);
37881     },
37882
37883         //private
37884     onRowUpdated : function(v, index, r){
37885         if(this.selection && this.selection.record == r){
37886             v.onCellSelect(index, this.selection.cell[1]);
37887         }
37888     },
37889
37890         //private
37891     onViewChange : function(){
37892         this.clearSelections(true);
37893     },
37894
37895         /**
37896          * Returns the currently selected cell,.
37897          * @return {Array} The selected cell (row, column) or null if none selected.
37898          */
37899     getSelectedCell : function(){
37900         return this.selection ? this.selection.cell : null;
37901     },
37902
37903     /**
37904      * Clears all selections.
37905      * @param {Boolean} true to prevent the gridview from being notified about the change.
37906      */
37907     clearSelections : function(preventNotify){
37908         var s = this.selection;
37909         if(s){
37910             if(preventNotify !== true){
37911                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
37912             }
37913             this.selection = null;
37914             this.fireEvent("selectionchange", this, null);
37915         }
37916     },
37917
37918     /**
37919      * Returns true if there is a selection.
37920      * @return {Boolean}
37921      */
37922     hasSelection : function(){
37923         return this.selection ? true : false;
37924     },
37925
37926     /** @ignore */
37927     handleMouseDown : function(e, t){
37928         var v = this.grid.getView();
37929         if(this.isLocked()){
37930             return;
37931         };
37932         var row = v.findRowIndex(t);
37933         var cell = v.findCellIndex(t);
37934         if(row !== false && cell !== false){
37935             this.select(row, cell);
37936         }
37937     },
37938
37939     /**
37940      * Selects a cell.
37941      * @param {Number} rowIndex
37942      * @param {Number} collIndex
37943      */
37944     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
37945         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
37946             this.clearSelections();
37947             r = r || this.grid.dataSource.getAt(rowIndex);
37948             this.selection = {
37949                 record : r,
37950                 cell : [rowIndex, colIndex]
37951             };
37952             if(!preventViewNotify){
37953                 var v = this.grid.getView();
37954                 v.onCellSelect(rowIndex, colIndex);
37955                 if(preventFocus !== true){
37956                     v.focusCell(rowIndex, colIndex);
37957                 }
37958             }
37959             this.fireEvent("cellselect", this, rowIndex, colIndex);
37960             this.fireEvent("selectionchange", this, this.selection);
37961         }
37962     },
37963
37964         //private
37965     isSelectable : function(rowIndex, colIndex, cm){
37966         return !cm.isHidden(colIndex);
37967     },
37968
37969     /** @ignore */
37970     handleKeyDown : function(e){
37971         //Roo.log('Cell Sel Model handleKeyDown');
37972         if(!e.isNavKeyPress()){
37973             return;
37974         }
37975         var g = this.grid, s = this.selection;
37976         if(!s){
37977             e.stopEvent();
37978             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
37979             if(cell){
37980                 this.select(cell[0], cell[1]);
37981             }
37982             return;
37983         }
37984         var sm = this;
37985         var walk = function(row, col, step){
37986             return g.walkCells(row, col, step, sm.isSelectable,  sm);
37987         };
37988         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
37989         var newCell;
37990
37991       
37992
37993         switch(k){
37994             case e.TAB:
37995                 // handled by onEditorKey
37996                 if (g.isEditor && g.editing) {
37997                     return;
37998                 }
37999                 if(e.shiftKey) {
38000                     newCell = walk(r, c-1, -1);
38001                 } else {
38002                     newCell = walk(r, c+1, 1);
38003                 }
38004                 break;
38005             
38006             case e.DOWN:
38007                newCell = walk(r+1, c, 1);
38008                 break;
38009             
38010             case e.UP:
38011                 newCell = walk(r-1, c, -1);
38012                 break;
38013             
38014             case e.RIGHT:
38015                 newCell = walk(r, c+1, 1);
38016                 break;
38017             
38018             case e.LEFT:
38019                 newCell = walk(r, c-1, -1);
38020                 break;
38021             
38022             case e.ENTER:
38023                 
38024                 if(g.isEditor && !g.editing){
38025                    g.startEditing(r, c);
38026                    e.stopEvent();
38027                    return;
38028                 }
38029                 
38030                 
38031              break;
38032         };
38033         if(newCell){
38034             this.select(newCell[0], newCell[1]);
38035             e.stopEvent();
38036             
38037         }
38038     },
38039
38040     acceptsNav : function(row, col, cm){
38041         return !cm.isHidden(col) && cm.isCellEditable(col, row);
38042     },
38043     /**
38044      * Selects a cell.
38045      * @param {Number} field (not used) - as it's normally used as a listener
38046      * @param {Number} e - event - fake it by using
38047      *
38048      * var e = Roo.EventObjectImpl.prototype;
38049      * e.keyCode = e.TAB
38050      *
38051      * 
38052      */
38053     onEditorKey : function(field, e){
38054         
38055         var k = e.getKey(),
38056             newCell,
38057             g = this.grid,
38058             ed = g.activeEditor,
38059             forward = false;
38060         ///Roo.log('onEditorKey' + k);
38061         
38062         
38063         if (this.enter_is_tab && k == e.ENTER) {
38064             k = e.TAB;
38065         }
38066         
38067         if(k == e.TAB){
38068             if(e.shiftKey){
38069                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
38070             }else{
38071                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
38072                 forward = true;
38073             }
38074             
38075             e.stopEvent();
38076             
38077         } else if(k == e.ENTER &&  !e.ctrlKey){
38078             ed.completeEdit();
38079             e.stopEvent();
38080             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
38081         
38082                 } else if(k == e.ESC){
38083             ed.cancelEdit();
38084         }
38085                 
38086         if (newCell) {
38087             var ecall = { cell : newCell, forward : forward };
38088             this.fireEvent('beforeeditnext', ecall );
38089             newCell = ecall.cell;
38090                         forward = ecall.forward;
38091         }
38092                 
38093         if(newCell){
38094             //Roo.log('next cell after edit');
38095             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
38096         } else if (forward) {
38097             // tabbed past last
38098             this.fireEvent.defer(100, this, ['tabend',this]);
38099         }
38100     }
38101 });/*
38102  * Based on:
38103  * Ext JS Library 1.1.1
38104  * Copyright(c) 2006-2007, Ext JS, LLC.
38105  *
38106  * Originally Released Under LGPL - original licence link has changed is not relivant.
38107  *
38108  * Fork - LGPL
38109  * <script type="text/javascript">
38110  */
38111  
38112 /**
38113  * @class Roo.grid.EditorGrid
38114  * @extends Roo.grid.Grid
38115  * Class for creating and editable grid.
38116  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
38117  * The container MUST have some type of size defined for the grid to fill. The container will be 
38118  * automatically set to position relative if it isn't already.
38119  * @param {Object} dataSource The data model to bind to
38120  * @param {Object} colModel The column model with info about this grid's columns
38121  */
38122 Roo.grid.EditorGrid = function(container, config){
38123     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
38124     this.getGridEl().addClass("xedit-grid");
38125
38126     if(!this.selModel){
38127         this.selModel = new Roo.grid.CellSelectionModel();
38128     }
38129
38130     this.activeEditor = null;
38131
38132         this.addEvents({
38133             /**
38134              * @event beforeedit
38135              * Fires before cell editing is triggered. The edit event object has the following properties <br />
38136              * <ul style="padding:5px;padding-left:16px;">
38137              * <li>grid - This grid</li>
38138              * <li>record - The record being edited</li>
38139              * <li>field - The field name being edited</li>
38140              * <li>value - The value for the field being edited.</li>
38141              * <li>row - The grid row index</li>
38142              * <li>column - The grid column index</li>
38143              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
38144              * </ul>
38145              * @param {Object} e An edit event (see above for description)
38146              */
38147             "beforeedit" : true,
38148             /**
38149              * @event afteredit
38150              * Fires after a cell is edited. <br />
38151              * <ul style="padding:5px;padding-left:16px;">
38152              * <li>grid - This grid</li>
38153              * <li>record - The record being edited</li>
38154              * <li>field - The field name being edited</li>
38155              * <li>value - The value being set</li>
38156              * <li>originalValue - The original value for the field, before the edit.</li>
38157              * <li>row - The grid row index</li>
38158              * <li>column - The grid column index</li>
38159              * </ul>
38160              * @param {Object} e An edit event (see above for description)
38161              */
38162             "afteredit" : true,
38163             /**
38164              * @event validateedit
38165              * Fires after a cell is edited, but before the value is set in the record. 
38166          * You can use this to modify the value being set in the field, Return false
38167              * to cancel the change. The edit event object has the following properties <br />
38168              * <ul style="padding:5px;padding-left:16px;">
38169          * <li>editor - This editor</li>
38170              * <li>grid - This grid</li>
38171              * <li>record - The record being edited</li>
38172              * <li>field - The field name being edited</li>
38173              * <li>value - The value being set</li>
38174              * <li>originalValue - The original value for the field, before the edit.</li>
38175              * <li>row - The grid row index</li>
38176              * <li>column - The grid column index</li>
38177              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
38178              * </ul>
38179              * @param {Object} e An edit event (see above for description)
38180              */
38181             "validateedit" : true
38182         });
38183     this.on("bodyscroll", this.stopEditing,  this);
38184     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
38185 };
38186
38187 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
38188     /**
38189      * @cfg {Number} clicksToEdit
38190      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
38191      */
38192     clicksToEdit: 2,
38193
38194     // private
38195     isEditor : true,
38196     // private
38197     trackMouseOver: false, // causes very odd FF errors
38198
38199     onCellDblClick : function(g, row, col){
38200         this.startEditing(row, col);
38201     },
38202
38203     onEditComplete : function(ed, value, startValue){
38204         this.editing = false;
38205         this.activeEditor = null;
38206         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
38207         var r = ed.record;
38208         var field = this.colModel.getDataIndex(ed.col);
38209         var e = {
38210             grid: this,
38211             record: r,
38212             field: field,
38213             originalValue: startValue,
38214             value: value,
38215             row: ed.row,
38216             column: ed.col,
38217             cancel:false,
38218             editor: ed
38219         };
38220         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
38221         cell.show();
38222           
38223         if(String(value) !== String(startValue)){
38224             
38225             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
38226                 r.set(field, e.value);
38227                 // if we are dealing with a combo box..
38228                 // then we also set the 'name' colum to be the displayField
38229                 if (ed.field.displayField && ed.field.name) {
38230                     r.set(ed.field.name, ed.field.el.dom.value);
38231                 }
38232                 
38233                 delete e.cancel; //?? why!!!
38234                 this.fireEvent("afteredit", e);
38235             }
38236         } else {
38237             this.fireEvent("afteredit", e); // always fire it!
38238         }
38239         this.view.focusCell(ed.row, ed.col);
38240     },
38241
38242     /**
38243      * Starts editing the specified for the specified row/column
38244      * @param {Number} rowIndex
38245      * @param {Number} colIndex
38246      */
38247     startEditing : function(row, col){
38248         this.stopEditing();
38249         if(this.colModel.isCellEditable(col, row)){
38250             this.view.ensureVisible(row, col, true);
38251           
38252             var r = this.dataSource.getAt(row);
38253             var field = this.colModel.getDataIndex(col);
38254             var cell = Roo.get(this.view.getCell(row,col));
38255             var e = {
38256                 grid: this,
38257                 record: r,
38258                 field: field,
38259                 value: r.data[field],
38260                 row: row,
38261                 column: col,
38262                 cancel:false 
38263             };
38264             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
38265                 this.editing = true;
38266                 var ed = this.colModel.getCellEditor(col, row);
38267                 
38268                 if (!ed) {
38269                     return;
38270                 }
38271                 if(!ed.rendered){
38272                     ed.render(ed.parentEl || document.body);
38273                 }
38274                 ed.field.reset();
38275                
38276                 cell.hide();
38277                 
38278                 (function(){ // complex but required for focus issues in safari, ie and opera
38279                     ed.row = row;
38280                     ed.col = col;
38281                     ed.record = r;
38282                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
38283                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
38284                     this.activeEditor = ed;
38285                     var v = r.data[field];
38286                     ed.startEdit(this.view.getCell(row, col), v);
38287                     // combo's with 'displayField and name set
38288                     if (ed.field.displayField && ed.field.name) {
38289                         ed.field.el.dom.value = r.data[ed.field.name];
38290                     }
38291                     
38292                     
38293                 }).defer(50, this);
38294             }
38295         }
38296     },
38297         
38298     /**
38299      * Stops any active editing
38300      */
38301     stopEditing : function(){
38302         if(this.activeEditor){
38303             this.activeEditor.completeEdit();
38304         }
38305         this.activeEditor = null;
38306     }
38307 });/*
38308  * Based on:
38309  * Ext JS Library 1.1.1
38310  * Copyright(c) 2006-2007, Ext JS, LLC.
38311  *
38312  * Originally Released Under LGPL - original licence link has changed is not relivant.
38313  *
38314  * Fork - LGPL
38315  * <script type="text/javascript">
38316  */
38317
38318 // private - not really -- you end up using it !
38319 // This is a support class used internally by the Grid components
38320
38321 /**
38322  * @class Roo.grid.GridEditor
38323  * @extends Roo.Editor
38324  * Class for creating and editable grid elements.
38325  * @param {Object} config any settings (must include field)
38326  */
38327 Roo.grid.GridEditor = function(field, config){
38328     if (!config && field.field) {
38329         config = field;
38330         field = Roo.factory(config.field, Roo.form);
38331     }
38332     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
38333     field.monitorTab = false;
38334 };
38335
38336 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
38337     
38338     /**
38339      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
38340      */
38341     
38342     alignment: "tl-tl",
38343     autoSize: "width",
38344     hideEl : false,
38345     cls: "x-small-editor x-grid-editor",
38346     shim:false,
38347     shadow:"frame"
38348 });/*
38349  * Based on:
38350  * Ext JS Library 1.1.1
38351  * Copyright(c) 2006-2007, Ext JS, LLC.
38352  *
38353  * Originally Released Under LGPL - original licence link has changed is not relivant.
38354  *
38355  * Fork - LGPL
38356  * <script type="text/javascript">
38357  */
38358   
38359
38360   
38361 Roo.grid.PropertyRecord = Roo.data.Record.create([
38362     {name:'name',type:'string'},  'value'
38363 ]);
38364
38365
38366 Roo.grid.PropertyStore = function(grid, source){
38367     this.grid = grid;
38368     this.store = new Roo.data.Store({
38369         recordType : Roo.grid.PropertyRecord
38370     });
38371     this.store.on('update', this.onUpdate,  this);
38372     if(source){
38373         this.setSource(source);
38374     }
38375     Roo.grid.PropertyStore.superclass.constructor.call(this);
38376 };
38377
38378
38379
38380 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
38381     setSource : function(o){
38382         this.source = o;
38383         this.store.removeAll();
38384         var data = [];
38385         for(var k in o){
38386             if(this.isEditableValue(o[k])){
38387                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
38388             }
38389         }
38390         this.store.loadRecords({records: data}, {}, true);
38391     },
38392
38393     onUpdate : function(ds, record, type){
38394         if(type == Roo.data.Record.EDIT){
38395             var v = record.data['value'];
38396             var oldValue = record.modified['value'];
38397             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
38398                 this.source[record.id] = v;
38399                 record.commit();
38400                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
38401             }else{
38402                 record.reject();
38403             }
38404         }
38405     },
38406
38407     getProperty : function(row){
38408        return this.store.getAt(row);
38409     },
38410
38411     isEditableValue: function(val){
38412         if(val && val instanceof Date){
38413             return true;
38414         }else if(typeof val == 'object' || typeof val == 'function'){
38415             return false;
38416         }
38417         return true;
38418     },
38419
38420     setValue : function(prop, value){
38421         this.source[prop] = value;
38422         this.store.getById(prop).set('value', value);
38423     },
38424
38425     getSource : function(){
38426         return this.source;
38427     }
38428 });
38429
38430 Roo.grid.PropertyColumnModel = function(grid, store){
38431     this.grid = grid;
38432     var g = Roo.grid;
38433     g.PropertyColumnModel.superclass.constructor.call(this, [
38434         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
38435         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
38436     ]);
38437     this.store = store;
38438     this.bselect = Roo.DomHelper.append(document.body, {
38439         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
38440             {tag: 'option', value: 'true', html: 'true'},
38441             {tag: 'option', value: 'false', html: 'false'}
38442         ]
38443     });
38444     Roo.id(this.bselect);
38445     var f = Roo.form;
38446     this.editors = {
38447         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
38448         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
38449         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
38450         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
38451         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
38452     };
38453     this.renderCellDelegate = this.renderCell.createDelegate(this);
38454     this.renderPropDelegate = this.renderProp.createDelegate(this);
38455 };
38456
38457 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
38458     
38459     
38460     nameText : 'Name',
38461     valueText : 'Value',
38462     
38463     dateFormat : 'm/j/Y',
38464     
38465     
38466     renderDate : function(dateVal){
38467         return dateVal.dateFormat(this.dateFormat);
38468     },
38469
38470     renderBool : function(bVal){
38471         return bVal ? 'true' : 'false';
38472     },
38473
38474     isCellEditable : function(colIndex, rowIndex){
38475         return colIndex == 1;
38476     },
38477
38478     getRenderer : function(col){
38479         return col == 1 ?
38480             this.renderCellDelegate : this.renderPropDelegate;
38481     },
38482
38483     renderProp : function(v){
38484         return this.getPropertyName(v);
38485     },
38486
38487     renderCell : function(val){
38488         var rv = val;
38489         if(val instanceof Date){
38490             rv = this.renderDate(val);
38491         }else if(typeof val == 'boolean'){
38492             rv = this.renderBool(val);
38493         }
38494         return Roo.util.Format.htmlEncode(rv);
38495     },
38496
38497     getPropertyName : function(name){
38498         var pn = this.grid.propertyNames;
38499         return pn && pn[name] ? pn[name] : name;
38500     },
38501
38502     getCellEditor : function(colIndex, rowIndex){
38503         var p = this.store.getProperty(rowIndex);
38504         var n = p.data['name'], val = p.data['value'];
38505         
38506         if(typeof(this.grid.customEditors[n]) == 'string'){
38507             return this.editors[this.grid.customEditors[n]];
38508         }
38509         if(typeof(this.grid.customEditors[n]) != 'undefined'){
38510             return this.grid.customEditors[n];
38511         }
38512         if(val instanceof Date){
38513             return this.editors['date'];
38514         }else if(typeof val == 'number'){
38515             return this.editors['number'];
38516         }else if(typeof val == 'boolean'){
38517             return this.editors['boolean'];
38518         }else{
38519             return this.editors['string'];
38520         }
38521     }
38522 });
38523
38524 /**
38525  * @class Roo.grid.PropertyGrid
38526  * @extends Roo.grid.EditorGrid
38527  * This class represents the  interface of a component based property grid control.
38528  * <br><br>Usage:<pre><code>
38529  var grid = new Roo.grid.PropertyGrid("my-container-id", {
38530       
38531  });
38532  // set any options
38533  grid.render();
38534  * </code></pre>
38535   
38536  * @constructor
38537  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
38538  * The container MUST have some type of size defined for the grid to fill. The container will be
38539  * automatically set to position relative if it isn't already.
38540  * @param {Object} config A config object that sets properties on this grid.
38541  */
38542 Roo.grid.PropertyGrid = function(container, config){
38543     config = config || {};
38544     var store = new Roo.grid.PropertyStore(this);
38545     this.store = store;
38546     var cm = new Roo.grid.PropertyColumnModel(this, store);
38547     store.store.sort('name', 'ASC');
38548     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
38549         ds: store.store,
38550         cm: cm,
38551         enableColLock:false,
38552         enableColumnMove:false,
38553         stripeRows:false,
38554         trackMouseOver: false,
38555         clicksToEdit:1
38556     }, config));
38557     this.getGridEl().addClass('x-props-grid');
38558     this.lastEditRow = null;
38559     this.on('columnresize', this.onColumnResize, this);
38560     this.addEvents({
38561          /**
38562              * @event beforepropertychange
38563              * Fires before a property changes (return false to stop?)
38564              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
38565              * @param {String} id Record Id
38566              * @param {String} newval New Value
38567          * @param {String} oldval Old Value
38568              */
38569         "beforepropertychange": true,
38570         /**
38571              * @event propertychange
38572              * Fires after a property changes
38573              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
38574              * @param {String} id Record Id
38575              * @param {String} newval New Value
38576          * @param {String} oldval Old Value
38577              */
38578         "propertychange": true
38579     });
38580     this.customEditors = this.customEditors || {};
38581 };
38582 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
38583     
38584      /**
38585      * @cfg {Object} customEditors map of colnames=> custom editors.
38586      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
38587      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
38588      * false disables editing of the field.
38589          */
38590     
38591       /**
38592      * @cfg {Object} propertyNames map of property Names to their displayed value
38593          */
38594     
38595     render : function(){
38596         Roo.grid.PropertyGrid.superclass.render.call(this);
38597         this.autoSize.defer(100, this);
38598     },
38599
38600     autoSize : function(){
38601         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
38602         if(this.view){
38603             this.view.fitColumns();
38604         }
38605     },
38606
38607     onColumnResize : function(){
38608         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
38609         this.autoSize();
38610     },
38611     /**
38612      * Sets the data for the Grid
38613      * accepts a Key => Value object of all the elements avaiable.
38614      * @param {Object} data  to appear in grid.
38615      */
38616     setSource : function(source){
38617         this.store.setSource(source);
38618         //this.autoSize();
38619     },
38620     /**
38621      * Gets all the data from the grid.
38622      * @return {Object} data  data stored in grid
38623      */
38624     getSource : function(){
38625         return this.store.getSource();
38626     }
38627 });/*
38628  * Based on:
38629  * Ext JS Library 1.1.1
38630  * Copyright(c) 2006-2007, Ext JS, LLC.
38631  *
38632  * Originally Released Under LGPL - original licence link has changed is not relivant.
38633  *
38634  * Fork - LGPL
38635  * <script type="text/javascript">
38636  */
38637  
38638 /**
38639  * @class Roo.LoadMask
38640  * A simple utility class for generically masking elements while loading data.  If the element being masked has
38641  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
38642  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
38643  * element's UpdateManager load indicator and will be destroyed after the initial load.
38644  * @constructor
38645  * Create a new LoadMask
38646  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
38647  * @param {Object} config The config object
38648  */
38649 Roo.LoadMask = function(el, config){
38650     this.el = Roo.get(el);
38651     Roo.apply(this, config);
38652     if(this.store){
38653         this.store.on('beforeload', this.onBeforeLoad, this);
38654         this.store.on('load', this.onLoad, this);
38655         this.store.on('loadexception', this.onLoadException, this);
38656         this.removeMask = false;
38657     }else{
38658         var um = this.el.getUpdateManager();
38659         um.showLoadIndicator = false; // disable the default indicator
38660         um.on('beforeupdate', this.onBeforeLoad, this);
38661         um.on('update', this.onLoad, this);
38662         um.on('failure', this.onLoad, this);
38663         this.removeMask = true;
38664     }
38665 };
38666
38667 Roo.LoadMask.prototype = {
38668     /**
38669      * @cfg {Boolean} removeMask
38670      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
38671      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
38672      */
38673     /**
38674      * @cfg {String} msg
38675      * The text to display in a centered loading message box (defaults to 'Loading...')
38676      */
38677     msg : 'Loading...',
38678     /**
38679      * @cfg {String} msgCls
38680      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
38681      */
38682     msgCls : 'x-mask-loading',
38683
38684     /**
38685      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
38686      * @type Boolean
38687      */
38688     disabled: false,
38689
38690     /**
38691      * Disables the mask to prevent it from being displayed
38692      */
38693     disable : function(){
38694        this.disabled = true;
38695     },
38696
38697     /**
38698      * Enables the mask so that it can be displayed
38699      */
38700     enable : function(){
38701         this.disabled = false;
38702     },
38703     
38704     onLoadException : function()
38705     {
38706         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
38707             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
38708         }
38709         this.el.unmask(this.removeMask);
38710     },
38711     // private
38712     onLoad : function()
38713     {
38714         this.el.unmask(this.removeMask);
38715     },
38716
38717     // private
38718     onBeforeLoad : function(){
38719         if(!this.disabled){
38720             this.el.mask(this.msg, this.msgCls);
38721         }
38722     },
38723
38724     // private
38725     destroy : function(){
38726         if(this.store){
38727             this.store.un('beforeload', this.onBeforeLoad, this);
38728             this.store.un('load', this.onLoad, this);
38729             this.store.un('loadexception', this.onLoadException, this);
38730         }else{
38731             var um = this.el.getUpdateManager();
38732             um.un('beforeupdate', this.onBeforeLoad, this);
38733             um.un('update', this.onLoad, this);
38734             um.un('failure', this.onLoad, this);
38735         }
38736     }
38737 };/*
38738  * Based on:
38739  * Ext JS Library 1.1.1
38740  * Copyright(c) 2006-2007, Ext JS, LLC.
38741  *
38742  * Originally Released Under LGPL - original licence link has changed is not relivant.
38743  *
38744  * Fork - LGPL
38745  * <script type="text/javascript">
38746  */
38747
38748
38749 /**
38750  * @class Roo.XTemplate
38751  * @extends Roo.Template
38752  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
38753 <pre><code>
38754 var t = new Roo.XTemplate(
38755         '&lt;select name="{name}"&gt;',
38756                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
38757         '&lt;/select&gt;'
38758 );
38759  
38760 // then append, applying the master template values
38761  </code></pre>
38762  *
38763  * Supported features:
38764  *
38765  *  Tags:
38766
38767 <pre><code>
38768       {a_variable} - output encoded.
38769       {a_variable.format:("Y-m-d")} - call a method on the variable
38770       {a_variable:raw} - unencoded output
38771       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
38772       {a_variable:this.method_on_template(...)} - call a method on the template object.
38773  
38774 </code></pre>
38775  *  The tpl tag:
38776 <pre><code>
38777         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
38778         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
38779         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
38780         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
38781   
38782         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
38783         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
38784 </code></pre>
38785  *      
38786  */
38787 Roo.XTemplate = function()
38788 {
38789     Roo.XTemplate.superclass.constructor.apply(this, arguments);
38790     if (this.html) {
38791         this.compile();
38792     }
38793 };
38794
38795
38796 Roo.extend(Roo.XTemplate, Roo.Template, {
38797
38798     /**
38799      * The various sub templates
38800      */
38801     tpls : false,
38802     /**
38803      *
38804      * basic tag replacing syntax
38805      * WORD:WORD()
38806      *
38807      * // you can fake an object call by doing this
38808      *  x.t:(test,tesT) 
38809      * 
38810      */
38811     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
38812
38813     /**
38814      * compile the template
38815      *
38816      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
38817      *
38818      */
38819     compile: function()
38820     {
38821         var s = this.html;
38822      
38823         s = ['<tpl>', s, '</tpl>'].join('');
38824     
38825         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
38826             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
38827             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
38828             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
38829             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
38830             m,
38831             id     = 0,
38832             tpls   = [];
38833     
38834         while(true == !!(m = s.match(re))){
38835             var forMatch   = m[0].match(nameRe),
38836                 ifMatch   = m[0].match(ifRe),
38837                 execMatch   = m[0].match(execRe),
38838                 namedMatch   = m[0].match(namedRe),
38839                 
38840                 exp  = null, 
38841                 fn   = null,
38842                 exec = null,
38843                 name = forMatch && forMatch[1] ? forMatch[1] : '';
38844                 
38845             if (ifMatch) {
38846                 // if - puts fn into test..
38847                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
38848                 if(exp){
38849                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
38850                 }
38851             }
38852             
38853             if (execMatch) {
38854                 // exec - calls a function... returns empty if true is  returned.
38855                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
38856                 if(exp){
38857                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
38858                 }
38859             }
38860             
38861             
38862             if (name) {
38863                 // for = 
38864                 switch(name){
38865                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
38866                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
38867                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
38868                 }
38869             }
38870             var uid = namedMatch ? namedMatch[1] : id;
38871             
38872             
38873             tpls.push({
38874                 id:     namedMatch ? namedMatch[1] : id,
38875                 target: name,
38876                 exec:   exec,
38877                 test:   fn,
38878                 body:   m[1] || ''
38879             });
38880             if (namedMatch) {
38881                 s = s.replace(m[0], '');
38882             } else { 
38883                 s = s.replace(m[0], '{xtpl'+ id + '}');
38884             }
38885             ++id;
38886         }
38887         this.tpls = [];
38888         for(var i = tpls.length-1; i >= 0; --i){
38889             this.compileTpl(tpls[i]);
38890             this.tpls[tpls[i].id] = tpls[i];
38891         }
38892         this.master = tpls[tpls.length-1];
38893         return this;
38894     },
38895     /**
38896      * same as applyTemplate, except it's done to one of the subTemplates
38897      * when using named templates, you can do:
38898      *
38899      * var str = pl.applySubTemplate('your-name', values);
38900      *
38901      * 
38902      * @param {Number} id of the template
38903      * @param {Object} values to apply to template
38904      * @param {Object} parent (normaly the instance of this object)
38905      */
38906     applySubTemplate : function(id, values, parent)
38907     {
38908         
38909         
38910         var t = this.tpls[id];
38911         
38912         
38913         try { 
38914             if(t.test && !t.test.call(this, values, parent)){
38915                 return '';
38916             }
38917         } catch(e) {
38918             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
38919             Roo.log(e.toString());
38920             Roo.log(t.test);
38921             return ''
38922         }
38923         try { 
38924             
38925             if(t.exec && t.exec.call(this, values, parent)){
38926                 return '';
38927             }
38928         } catch(e) {
38929             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
38930             Roo.log(e.toString());
38931             Roo.log(t.exec);
38932             return ''
38933         }
38934         try {
38935             var vs = t.target ? t.target.call(this, values, parent) : values;
38936             parent = t.target ? values : parent;
38937             if(t.target && vs instanceof Array){
38938                 var buf = [];
38939                 for(var i = 0, len = vs.length; i < len; i++){
38940                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
38941                 }
38942                 return buf.join('');
38943             }
38944             return t.compiled.call(this, vs, parent);
38945         } catch (e) {
38946             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
38947             Roo.log(e.toString());
38948             Roo.log(t.compiled);
38949             return '';
38950         }
38951     },
38952
38953     compileTpl : function(tpl)
38954     {
38955         var fm = Roo.util.Format;
38956         var useF = this.disableFormats !== true;
38957         var sep = Roo.isGecko ? "+" : ",";
38958         var undef = function(str) {
38959             Roo.log("Property not found :"  + str);
38960             return '';
38961         };
38962         
38963         var fn = function(m, name, format, args)
38964         {
38965             //Roo.log(arguments);
38966             args = args ? args.replace(/\\'/g,"'") : args;
38967             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
38968             if (typeof(format) == 'undefined') {
38969                 format= 'htmlEncode';
38970             }
38971             if (format == 'raw' ) {
38972                 format = false;
38973             }
38974             
38975             if(name.substr(0, 4) == 'xtpl'){
38976                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
38977             }
38978             
38979             // build an array of options to determine if value is undefined..
38980             
38981             // basically get 'xxxx.yyyy' then do
38982             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
38983             //    (function () { Roo.log("Property not found"); return ''; })() :
38984             //    ......
38985             
38986             var udef_ar = [];
38987             var lookfor = '';
38988             Roo.each(name.split('.'), function(st) {
38989                 lookfor += (lookfor.length ? '.': '') + st;
38990                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
38991             });
38992             
38993             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
38994             
38995             
38996             if(format && useF){
38997                 
38998                 args = args ? ',' + args : "";
38999                  
39000                 if(format.substr(0, 5) != "this."){
39001                     format = "fm." + format + '(';
39002                 }else{
39003                     format = 'this.call("'+ format.substr(5) + '", ';
39004                     args = ", values";
39005                 }
39006                 
39007                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
39008             }
39009              
39010             if (args.length) {
39011                 // called with xxyx.yuu:(test,test)
39012                 // change to ()
39013                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
39014             }
39015             // raw.. - :raw modifier..
39016             return "'"+ sep + udef_st  + name + ")"+sep+"'";
39017             
39018         };
39019         var body;
39020         // branched to use + in gecko and [].join() in others
39021         if(Roo.isGecko){
39022             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
39023                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
39024                     "';};};";
39025         }else{
39026             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
39027             body.push(tpl.body.replace(/(\r\n|\n)/g,
39028                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
39029             body.push("'].join('');};};");
39030             body = body.join('');
39031         }
39032         
39033         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
39034        
39035         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
39036         eval(body);
39037         
39038         return this;
39039     },
39040
39041     applyTemplate : function(values){
39042         return this.master.compiled.call(this, values, {});
39043         //var s = this.subs;
39044     },
39045
39046     apply : function(){
39047         return this.applyTemplate.apply(this, arguments);
39048     }
39049
39050  });
39051
39052 Roo.XTemplate.from = function(el){
39053     el = Roo.getDom(el);
39054     return new Roo.XTemplate(el.value || el.innerHTML);
39055 };/*
39056  * Original code for Roojs - LGPL
39057  * <script type="text/javascript">
39058  */
39059  
39060 /**
39061  * @class Roo.XComponent
39062  * A delayed Element creator...
39063  * Or a way to group chunks of interface together.
39064  * 
39065  * Mypart.xyx = new Roo.XComponent({
39066
39067     parent : 'Mypart.xyz', // empty == document.element.!!
39068     order : '001',
39069     name : 'xxxx'
39070     region : 'xxxx'
39071     disabled : function() {} 
39072      
39073     tree : function() { // return an tree of xtype declared components
39074         var MODULE = this;
39075         return 
39076         {
39077             xtype : 'NestedLayoutPanel',
39078             // technicall
39079         }
39080      ]
39081  *})
39082  *
39083  *
39084  * It can be used to build a big heiracy, with parent etc.
39085  * or you can just use this to render a single compoent to a dom element
39086  * MYPART.render(Roo.Element | String(id) | dom_element )
39087  * 
39088  * @extends Roo.util.Observable
39089  * @constructor
39090  * @param cfg {Object} configuration of component
39091  * 
39092  */
39093 Roo.XComponent = function(cfg) {
39094     Roo.apply(this, cfg);
39095     this.addEvents({ 
39096         /**
39097              * @event built
39098              * Fires when this the componnt is built
39099              * @param {Roo.XComponent} c the component
39100              */
39101         'built' : true
39102         
39103     });
39104     this.region = this.region || 'center'; // default..
39105     Roo.XComponent.register(this);
39106     this.modules = false;
39107     this.el = false; // where the layout goes..
39108     
39109     
39110 }
39111 Roo.extend(Roo.XComponent, Roo.util.Observable, {
39112     /**
39113      * @property el
39114      * The created element (with Roo.factory())
39115      * @type {Roo.Layout}
39116      */
39117     el  : false,
39118     
39119     /**
39120      * @property el
39121      * for BC  - use el in new code
39122      * @type {Roo.Layout}
39123      */
39124     panel : false,
39125     
39126     /**
39127      * @property layout
39128      * for BC  - use el in new code
39129      * @type {Roo.Layout}
39130      */
39131     layout : false,
39132     
39133      /**
39134      * @cfg {Function|boolean} disabled
39135      * If this module is disabled by some rule, return true from the funtion
39136      */
39137     disabled : false,
39138     
39139     /**
39140      * @cfg {String} parent 
39141      * Name of parent element which it get xtype added to..
39142      */
39143     parent: false,
39144     
39145     /**
39146      * @cfg {String} order
39147      * Used to set the order in which elements are created (usefull for multiple tabs)
39148      */
39149     
39150     order : false,
39151     /**
39152      * @cfg {String} name
39153      * String to display while loading.
39154      */
39155     name : false,
39156     /**
39157      * @cfg {String} region
39158      * Region to render component to (defaults to center)
39159      */
39160     region : 'center',
39161     
39162     /**
39163      * @cfg {Array} items
39164      * A single item array - the first element is the root of the tree..
39165      * It's done this way to stay compatible with the Xtype system...
39166      */
39167     items : false,
39168     
39169     /**
39170      * @property _tree
39171      * The method that retuns the tree of parts that make up this compoennt 
39172      * @type {function}
39173      */
39174     _tree  : false,
39175     
39176      /**
39177      * render
39178      * render element to dom or tree
39179      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
39180      */
39181     
39182     render : function(el)
39183     {
39184         
39185         el = el || false;
39186         var hp = this.parent ? 1 : 0;
39187         
39188         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
39189             // if parent is a '#.....' string, then let's use that..
39190             var ename = this.parent.substr(1)
39191             this.parent = false;
39192             el = Roo.get(ename);
39193             if (!el) {
39194                 Roo.log("Warning - element can not be found :#" + ename );
39195                 return;
39196             }
39197         }
39198         
39199         
39200         if (!this.parent) {
39201             
39202             el = el ? Roo.get(el) : false;      
39203             
39204             // it's a top level one..
39205             this.parent =  {
39206                 el : new Roo.BorderLayout(el || document.body, {
39207                 
39208                      center: {
39209                          titlebar: false,
39210                          autoScroll:false,
39211                          closeOnTab: true,
39212                          tabPosition: 'top',
39213                           //resizeTabs: true,
39214                          alwaysShowTabs: el && hp? false :  true,
39215                          hideTabs: el || !hp ? true :  false,
39216                          minTabWidth: 140
39217                      }
39218                  })
39219             }
39220         }
39221         
39222                 if (!this.parent.el) {
39223                         // probably an old style ctor, which has been disabled.
39224                         return;
39225                         
39226                 }
39227                 // The 'tree' method is  '_tree now' 
39228             
39229         var tree = this._tree ? this._tree() : this.tree();
39230         tree.region = tree.region || this.region;
39231         this.el = this.parent.el.addxtype(tree);
39232         this.fireEvent('built', this);
39233         
39234         this.panel = this.el;
39235         this.layout = this.panel.layout;
39236                 this.parentLayout = this.parent.layout  || false;  
39237          
39238     }
39239     
39240 });
39241
39242 Roo.apply(Roo.XComponent, {
39243     /**
39244      * @property  hideProgress
39245      * true to disable the building progress bar.. usefull on single page renders.
39246      * @type Boolean
39247      */
39248     hideProgress : false,
39249     /**
39250      * @property  buildCompleted
39251      * True when the builder has completed building the interface.
39252      * @type Boolean
39253      */
39254     buildCompleted : false,
39255      
39256     /**
39257      * @property  topModule
39258      * the upper most module - uses document.element as it's constructor.
39259      * @type Object
39260      */
39261      
39262     topModule  : false,
39263       
39264     /**
39265      * @property  modules
39266      * array of modules to be created by registration system.
39267      * @type {Array} of Roo.XComponent
39268      */
39269     
39270     modules : [],
39271     /**
39272      * @property  elmodules
39273      * array of modules to be created by which use #ID 
39274      * @type {Array} of Roo.XComponent
39275      */
39276      
39277     elmodules : [],
39278
39279     
39280     /**
39281      * Register components to be built later.
39282      *
39283      * This solves the following issues
39284      * - Building is not done on page load, but after an authentication process has occured.
39285      * - Interface elements are registered on page load
39286      * - Parent Interface elements may not be loaded before child, so this handles that..
39287      * 
39288      *
39289      * example:
39290      * 
39291      * MyApp.register({
39292           order : '000001',
39293           module : 'Pman.Tab.projectMgr',
39294           region : 'center',
39295           parent : 'Pman.layout',
39296           disabled : false,  // or use a function..
39297         })
39298      
39299      * * @param {Object} details about module
39300      */
39301     register : function(obj) {
39302                 
39303         Roo.XComponent.event.fireEvent('register', obj);
39304         switch(typeof(obj.disabled) ) {
39305                 
39306             case 'undefined':
39307                 break;
39308             
39309             case 'function':
39310                 if ( obj.disabled() ) {
39311                         return;
39312                 }
39313                 break;
39314             
39315             default:
39316                 if (obj.disabled) {
39317                         return;
39318                 }
39319                 break;
39320         }
39321                 
39322         this.modules.push(obj);
39323          
39324     },
39325     /**
39326      * convert a string to an object..
39327      * eg. 'AAA.BBB' -> finds AAA.BBB
39328
39329      */
39330     
39331     toObject : function(str)
39332     {
39333         if (!str || typeof(str) == 'object') {
39334             return str;
39335         }
39336         if (str.substring(0,1) == '#') {
39337             return str;
39338         }
39339
39340         var ar = str.split('.');
39341         var rt, o;
39342         rt = ar.shift();
39343             /** eval:var:o */
39344         try {
39345             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
39346         } catch (e) {
39347             throw "Module not found : " + str;
39348         }
39349         
39350         if (o === false) {
39351             throw "Module not found : " + str;
39352         }
39353         Roo.each(ar, function(e) {
39354             if (typeof(o[e]) == 'undefined') {
39355                 throw "Module not found : " + str;
39356             }
39357             o = o[e];
39358         });
39359         
39360         return o;
39361         
39362     },
39363     
39364     
39365     /**
39366      * move modules into their correct place in the tree..
39367      * 
39368      */
39369     preBuild : function ()
39370     {
39371         var _t = this;
39372         Roo.each(this.modules , function (obj)
39373         {
39374             Roo.XComponent.event.fireEvent('beforebuild', obj);
39375             
39376             var opar = obj.parent;
39377             try { 
39378                 obj.parent = this.toObject(opar);
39379             } catch(e) {
39380                 Roo.log("parent:toObject failed: " + e.toString());
39381                 return;
39382             }
39383             
39384             if (!obj.parent) {
39385                 Roo.debug && Roo.log("GOT top level module");
39386                 Roo.debug && Roo.log(obj);
39387                 obj.modules = new Roo.util.MixedCollection(false, 
39388                     function(o) { return o.order + '' }
39389                 );
39390                 this.topModule = obj;
39391                 return;
39392             }
39393                         // parent is a string (usually a dom element name..)
39394             if (typeof(obj.parent) == 'string') {
39395                 this.elmodules.push(obj);
39396                 return;
39397             }
39398             if (obj.parent.constructor != Roo.XComponent) {
39399                 Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
39400             }
39401             if (!obj.parent.modules) {
39402                 obj.parent.modules = new Roo.util.MixedCollection(false, 
39403                     function(o) { return o.order + '' }
39404                 );
39405             }
39406             if (obj.parent.disabled) {
39407                 obj.disabled = true;
39408             }
39409             obj.parent.modules.add(obj);
39410         }, this);
39411     },
39412     
39413      /**
39414      * make a list of modules to build.
39415      * @return {Array} list of modules. 
39416      */ 
39417     
39418     buildOrder : function()
39419     {
39420         var _this = this;
39421         var cmp = function(a,b) {   
39422             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
39423         };
39424         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
39425             throw "No top level modules to build";
39426         }
39427         
39428         // make a flat list in order of modules to build.
39429         var mods = this.topModule ? [ this.topModule ] : [];
39430                 
39431         // elmodules (is a list of DOM based modules )
39432         Roo.each(this.elmodules, function(e) {
39433             mods.push(e)
39434         });
39435
39436         
39437         // add modules to their parents..
39438         var addMod = function(m) {
39439             Roo.debug && Roo.log("build Order: add: " + m.name);
39440             
39441         mods.push(m);
39442         if (m.modules && !m.disabled) {
39443             Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
39444             m.modules.keySort('ASC',  cmp );
39445             Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
39446
39447             m.modules.each(addMod);
39448         } else {
39449             Roo.debug && Roo.log("build Order: no child modules");
39450             }
39451             // not sure if this is used any more..
39452             if (m.finalize) {
39453                 m.finalize.name = m.name + " (clean up) ";
39454                 mods.push(m.finalize);
39455             }
39456             
39457         }
39458         if (this.topModule) { 
39459             this.topModule.modules.keySort('ASC',  cmp );
39460             this.topModule.modules.each(addMod);
39461         }
39462         return mods;
39463     },
39464     
39465      /**
39466      * Build the registered modules.
39467      * @param {Object} parent element.
39468      * @param {Function} optional method to call after module has been added.
39469      * 
39470      */ 
39471    
39472     build : function() 
39473     {
39474         
39475         this.preBuild();
39476         var mods = this.buildOrder();
39477       
39478         //this.allmods = mods;
39479         //Roo.debug && Roo.log(mods);
39480         //return;
39481         if (!mods.length) { // should not happen
39482             throw "NO modules!!!";
39483         }
39484         
39485         
39486         var msg = "Building Interface...";
39487         // flash it up as modal - so we store the mask!?
39488         if (!this.hideProgress) {
39489             Roo.MessageBox.show({ title: 'loading' });
39490             Roo.MessageBox.show({
39491                title: "Please wait...",
39492                msg: msg,
39493                width:450,
39494                progress:true,
39495                closable:false,
39496                modal: false
39497               
39498             });
39499         }
39500         var total = mods.length;
39501         
39502         var _this = this;
39503         var progressRun = function() {
39504             if (!mods.length) {
39505                 Roo.debug && Roo.log('hide?');
39506                 if (!this.hideProgress) {
39507                     Roo.MessageBox.hide();
39508                 }
39509                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
39510                 
39511                 // THE END...
39512                 return false;   
39513             }
39514             
39515             var m = mods.shift();
39516             
39517             
39518             Roo.debug && Roo.log(m);
39519             // not sure if this is supported any more.. - modules that are are just function
39520             if (typeof(m) == 'function') { 
39521                 m.call(this);
39522                 return progressRun.defer(10, _this);
39523             } 
39524             
39525             
39526             msg = "Building Interface " + (total  - mods.length) + 
39527                     " of " + total + 
39528                     (m.name ? (' - ' + m.name) : '');
39529                         Roo.debug && Roo.log(msg);
39530             if (!this.hideProgress) { 
39531                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
39532             }
39533             
39534          
39535             // is the module disabled?
39536             var disabled = (typeof(m.disabled) == 'function') ?
39537                 m.disabled.call(m.module.disabled) : m.disabled;    
39538             
39539             
39540             if (disabled) {
39541                 return progressRun(); // we do not update the display!
39542             }
39543             
39544             // now build 
39545             
39546                         
39547                         
39548             m.render();
39549             // it's 10 on top level, and 1 on others??? why...
39550             return progressRun.defer(10, _this);
39551              
39552         }
39553         progressRun.defer(1, _this);
39554      
39555         
39556         
39557     },
39558         
39559         
39560         /**
39561          * Event Object.
39562          *
39563          *
39564          */
39565         event: false, 
39566     /**
39567          * wrapper for event.on - aliased later..  
39568          * Typically use to register a event handler for register:
39569          *
39570          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
39571          *
39572          */
39573     on : false
39574    
39575     
39576     
39577 });
39578
39579 Roo.XComponent.event = new Roo.util.Observable({
39580                 events : { 
39581                         /**
39582                          * @event register
39583                          * Fires when an Component is registered,
39584                          * set the disable property on the Component to stop registration.
39585                          * @param {Roo.XComponent} c the component being registerd.
39586                          * 
39587                          */
39588                         'register' : true,
39589             /**
39590                          * @event beforebuild
39591                          * Fires before each Component is built
39592                          * can be used to apply permissions.
39593                          * @param {Roo.XComponent} c the component being registerd.
39594                          * 
39595                          */
39596                         'beforebuild' : true,
39597                         /**
39598                          * @event buildcomplete
39599                          * Fires on the top level element when all elements have been built
39600                          * @param {Roo.XComponent} the top level component.
39601                          */
39602                         'buildcomplete' : true
39603                         
39604                 }
39605 });
39606
39607 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
39608  //<script type="text/javascript">
39609
39610
39611 /**
39612  * @class Roo.Login
39613  * @extends Roo.LayoutDialog
39614  * A generic Login Dialog..... - only one needed in theory!?!?
39615  *
39616  * Fires XComponent builder on success...
39617  * 
39618  * Sends 
39619  *    username,password, lang = for login actions.
39620  *    check = 1 for periodic checking that sesion is valid.
39621  *    passwordRequest = email request password
39622  *    logout = 1 = to logout
39623  * 
39624  * Affects: (this id="????" elements)
39625  *   loading  (removed) (used to indicate application is loading)
39626  *   loading-mask (hides) (used to hide application when it's building loading)
39627  *   
39628  * 
39629  * Usage: 
39630  *    
39631  * 
39632  * Myapp.login = Roo.Login({
39633      url: xxxx,
39634    
39635      realm : 'Myapp', 
39636      
39637      
39638      method : 'POST',
39639      
39640      
39641      * 
39642  })
39643  * 
39644  * 
39645  * 
39646  **/
39647  
39648 Roo.Login = function(cfg)
39649 {
39650     this.addEvents({
39651         'refreshed' : true
39652     });
39653     
39654     Roo.apply(this,cfg);
39655     
39656     Roo.onReady(function() {
39657         this.onLoad();
39658     }, this);
39659     // call parent..
39660     
39661    
39662     Roo.Login.superclass.constructor.call(this, this);
39663     //this.addxtype(this.items[0]);
39664     
39665     
39666 }
39667
39668
39669 Roo.extend(Roo.Login, Roo.LayoutDialog, {
39670     
39671     /**
39672      * @cfg {String} method
39673      * Method used to query for login details.
39674      */
39675     
39676     method : 'POST',
39677     /**
39678      * @cfg {String} url
39679      * URL to query login data. - eg. baseURL + '/Login.php'
39680      */
39681     url : '',
39682     
39683     /**
39684      * @property user
39685      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
39686      * @type {Object} 
39687      */
39688     user : false,
39689     /**
39690      * @property checkFails
39691      * Number of times we have attempted to get authentication check, and failed.
39692      * @type {Number} 
39693      */
39694     checkFails : 0,
39695       /**
39696      * @property intervalID
39697      * The window interval that does the constant login checking.
39698      * @type {Number} 
39699      */
39700     intervalID : 0,
39701     
39702     
39703     onLoad : function() // called on page load...
39704     {
39705         // load 
39706          
39707         if (Roo.get('loading')) { // clear any loading indicator..
39708             Roo.get('loading').remove();
39709         }
39710         
39711         //this.switchLang('en'); // set the language to english..
39712        
39713         this.check({
39714             success:  function(response, opts)  {  // check successfull...
39715             
39716                 var res = this.processResponse(response);
39717                 this.checkFails =0;
39718                 if (!res.success) { // error!
39719                     this.checkFails = 5;
39720                     //console.log('call failure');
39721                     return this.failure(response,opts);
39722                 }
39723                 
39724                 if (!res.data.id) { // id=0 == login failure.
39725                     return this.show();
39726                 }
39727                 
39728                               
39729                         //console.log(success);
39730                 this.fillAuth(res.data);   
39731                 this.checkFails =0;
39732                 Roo.XComponent.build();
39733             },
39734             failure : this.show
39735         });
39736         
39737     }, 
39738     
39739     
39740     check: function(cfg) // called every so often to refresh cookie etc..
39741     {
39742         if (cfg.again) { // could be undefined..
39743             this.checkFails++;
39744         } else {
39745             this.checkFails = 0;
39746         }
39747         var _this = this;
39748         if (this.sending) {
39749             if ( this.checkFails > 4) {
39750                 Roo.MessageBox.alert("Error",  
39751                     "Error getting authentication status. - try reloading, or wait a while", function() {
39752                         _this.sending = false;
39753                     }); 
39754                 return;
39755             }
39756             cfg.again = true;
39757             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
39758             return;
39759         }
39760         this.sending = true;
39761         
39762         Roo.Ajax.request({  
39763             url: this.url,
39764             params: {
39765                 getAuthUser: true
39766             },  
39767             method: this.method,
39768             success:  cfg.success || this.success,
39769             failure : cfg.failure || this.failure,
39770             scope : this,
39771             callCfg : cfg
39772               
39773         });  
39774     }, 
39775     
39776     
39777     logout: function()
39778     {
39779         window.onbeforeunload = function() { }; // false does not work for IE..
39780         this.user = false;
39781         var _this = this;
39782         
39783         Roo.Ajax.request({  
39784             url: this.url,
39785             params: {
39786                 logout: 1
39787             },  
39788             method: 'GET',
39789             failure : function() {
39790                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
39791                     document.location = document.location.toString() + '?ts=' + Math.random();
39792                 });
39793                 
39794             },
39795             success : function() {
39796                 _this.user = false;
39797                 this.checkFails =0;
39798                 // fixme..
39799                 document.location = document.location.toString() + '?ts=' + Math.random();
39800             }
39801               
39802               
39803         }); 
39804     },
39805     
39806     processResponse : function (response)
39807     {
39808         var res = '';
39809         try {
39810             res = Roo.decode(response.responseText);
39811             // oops...
39812             if (typeof(res) != 'object') {
39813                 res = { success : false, errorMsg : res, errors : true };
39814             }
39815             if (typeof(res.success) == 'undefined') {
39816                 res.success = false;
39817             }
39818             
39819         } catch(e) {
39820             res = { success : false,  errorMsg : response.responseText, errors : true };
39821         }
39822         return res;
39823     },
39824     
39825     success : function(response, opts)  // check successfull...
39826     {  
39827         this.sending = false;
39828         var res = this.processResponse(response);
39829         if (!res.success) {
39830             return this.failure(response, opts);
39831         }
39832         if (!res.data || !res.data.id) {
39833             return this.failure(response,opts);
39834         }
39835         //console.log(res);
39836         this.fillAuth(res.data);
39837         
39838         this.checkFails =0;
39839         
39840     },
39841     
39842     
39843     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
39844     {
39845         this.authUser = -1;
39846         this.sending = false;
39847         var res = this.processResponse(response);
39848         //console.log(res);
39849         if ( this.checkFails > 2) {
39850         
39851             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
39852                 "Error getting authentication status. - try reloading"); 
39853             return;
39854         }
39855         opts.callCfg.again = true;
39856         this.check.defer(1000, this, [ opts.callCfg ]);
39857         return;  
39858     },
39859     
39860     
39861     
39862     fillAuth: function(au) {
39863         this.startAuthCheck();
39864         this.authUserId = au.id;
39865         this.authUser = au;
39866         this.lastChecked = new Date();
39867         this.fireEvent('refreshed', au);
39868         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
39869         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
39870         au.lang = au.lang || 'en';
39871         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
39872         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
39873         this.switchLang(au.lang );
39874         
39875      
39876         // open system... - -on setyp..
39877         if (this.authUserId  < 0) {
39878             Roo.MessageBox.alert("Warning", 
39879                 "This is an open system - please set up a admin user with a password.");  
39880         }
39881          
39882         //Pman.onload(); // which should do nothing if it's a re-auth result...
39883         
39884              
39885     },
39886     
39887     startAuthCheck : function() // starter for timeout checking..
39888     {
39889         if (this.intervalID) { // timer already in place...
39890             return false;
39891         }
39892         var _this = this;
39893         this.intervalID =  window.setInterval(function() {
39894               _this.check(false);
39895             }, 120000); // every 120 secs = 2mins..
39896         
39897         
39898     },
39899          
39900     
39901     switchLang : function (lang) 
39902     {
39903         _T = typeof(_T) == 'undefined' ? false : _T;
39904           if (!_T || !lang.length) {
39905             return;
39906         }
39907         
39908         if (!_T && lang != 'en') {
39909             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
39910             return;
39911         }
39912         
39913         if (typeof(_T.en) == 'undefined') {
39914             _T.en = {};
39915             Roo.apply(_T.en, _T);
39916         }
39917         
39918         if (typeof(_T[lang]) == 'undefined') {
39919             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
39920             return;
39921         }
39922         
39923         
39924         Roo.apply(_T, _T[lang]);
39925         // just need to set the text values for everything...
39926         var _this = this;
39927         /* this will not work ...
39928         if (this.form) { 
39929             
39930                
39931             function formLabel(name, val) {
39932                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
39933             }
39934             
39935             formLabel('password', "Password"+':');
39936             formLabel('username', "Email Address"+':');
39937             formLabel('lang', "Language"+':');
39938             this.dialog.setTitle("Login");
39939             this.dialog.buttons[0].setText("Forgot Password");
39940             this.dialog.buttons[1].setText("Login");
39941         }
39942         */
39943         
39944         
39945     },
39946     
39947     
39948     title: "Login",
39949     modal: true,
39950     width:  350,
39951     //height: 230,
39952     height: 180,
39953     shadow: true,
39954     minWidth:200,
39955     minHeight:180,
39956     //proxyDrag: true,
39957     closable: false,
39958     draggable: false,
39959     collapsible: false,
39960     resizable: false,
39961     center: {  // needed??
39962         autoScroll:false,
39963         titlebar: false,
39964        // tabPosition: 'top',
39965         hideTabs: true,
39966         closeOnTab: true,
39967         alwaysShowTabs: false
39968     } ,
39969     listeners : {
39970         
39971         show  : function(dlg)
39972         {
39973             //console.log(this);
39974             this.form = this.layout.getRegion('center').activePanel.form;
39975             this.form.dialog = dlg;
39976             this.buttons[0].form = this.form;
39977             this.buttons[0].dialog = dlg;
39978             this.buttons[1].form = this.form;
39979             this.buttons[1].dialog = dlg;
39980            
39981            //this.resizeToLogo.defer(1000,this);
39982             // this is all related to resizing for logos..
39983             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
39984            //// if (!sz) {
39985              //   this.resizeToLogo.defer(1000,this);
39986              //   return;
39987            // }
39988             //var w = Ext.lib.Dom.getViewWidth() - 100;
39989             //var h = Ext.lib.Dom.getViewHeight() - 100;
39990             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
39991             //this.center();
39992             if (this.disabled) {
39993                 this.hide();
39994                 return;
39995             }
39996             
39997             if (this.user.id < 0) { // used for inital setup situations.
39998                 return;
39999             }
40000             
40001             if (this.intervalID) {
40002                 // remove the timer
40003                 window.clearInterval(this.intervalID);
40004                 this.intervalID = false;
40005             }
40006             
40007             
40008             if (Roo.get('loading')) {
40009                 Roo.get('loading').remove();
40010             }
40011             if (Roo.get('loading-mask')) {
40012                 Roo.get('loading-mask').hide();
40013             }
40014             
40015             //incomming._node = tnode;
40016             this.form.reset();
40017             //this.dialog.modal = !modal;
40018             //this.dialog.show();
40019             this.el.unmask(); 
40020             
40021             
40022             this.form.setValues({
40023                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
40024                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
40025             });
40026             
40027             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
40028             if (this.form.findField('username').getValue().length > 0 ){
40029                 this.form.findField('password').focus();
40030             } else {
40031                this.form.findField('username').focus();
40032             }
40033     
40034         }
40035     },
40036     items : [
40037          {
40038        
40039             xtype : 'ContentPanel',
40040             xns : Roo,
40041             region: 'center',
40042             fitToFrame : true,
40043             
40044             items : [
40045     
40046                 {
40047                
40048                     xtype : 'Form',
40049                     xns : Roo.form,
40050                     labelWidth: 100,
40051                     style : 'margin: 10px;',
40052                     
40053                     listeners : {
40054                         actionfailed : function(f, act) {
40055                             // form can return { errors: .... }
40056                                 
40057                             //act.result.errors // invalid form element list...
40058                             //act.result.errorMsg// invalid form element list...
40059                             
40060                             this.dialog.el.unmask();
40061                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
40062                                         "Login failed - communication error - try again.");
40063                                       
40064                         },
40065                         actioncomplete: function(re, act) {
40066                              
40067                             Roo.state.Manager.set(
40068                                 this.dialog.realm + '.username',  
40069                                     this.findField('username').getValue()
40070                             );
40071                             Roo.state.Manager.set(
40072                                 this.dialog.realm + '.lang',  
40073                                 this.findField('lang').getValue() 
40074                             );
40075                             
40076                             this.dialog.fillAuth(act.result.data);
40077                               
40078                             this.dialog.hide();
40079                             
40080                             if (Roo.get('loading-mask')) {
40081                                 Roo.get('loading-mask').show();
40082                             }
40083                             Roo.XComponent.build();
40084                             
40085                              
40086                             
40087                         }
40088                     },
40089                     items : [
40090                         {
40091                             xtype : 'TextField',
40092                             xns : Roo.form,
40093                             fieldLabel: "Email Address",
40094                             name: 'username',
40095                             width:200,
40096                             autoCreate : {tag: "input", type: "text", size: "20"}
40097                         },
40098                         {
40099                             xtype : 'TextField',
40100                             xns : Roo.form,
40101                             fieldLabel: "Password",
40102                             inputType: 'password',
40103                             name: 'password',
40104                             width:200,
40105                             autoCreate : {tag: "input", type: "text", size: "20"},
40106                             listeners : {
40107                                 specialkey : function(e,ev) {
40108                                     if (ev.keyCode == 13) {
40109                                         this.form.dialog.el.mask("Logging in");
40110                                         this.form.doAction('submit', {
40111                                             url: this.form.dialog.url,
40112                                             method: this.form.dialog.method
40113                                         });
40114                                     }
40115                                 }
40116                             }  
40117                         },
40118                         {
40119                             xtype : 'ComboBox',
40120                             xns : Roo.form,
40121                             fieldLabel: "Language",
40122                             name : 'langdisp',
40123                             store: {
40124                                 xtype : 'SimpleStore',
40125                                 fields: ['lang', 'ldisp'],
40126                                 data : [
40127                                     [ 'en', 'English' ],
40128                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
40129                                     [ 'zh_CN', '\u7C21\u4E2D' ]
40130                                 ]
40131                             },
40132                             
40133                             valueField : 'lang',
40134                             hiddenName:  'lang',
40135                             width: 200,
40136                             displayField:'ldisp',
40137                             typeAhead: false,
40138                             editable: false,
40139                             mode: 'local',
40140                             triggerAction: 'all',
40141                             emptyText:'Select a Language...',
40142                             selectOnFocus:true,
40143                             listeners : {
40144                                 select :  function(cb, rec, ix) {
40145                                     this.form.switchLang(rec.data.lang);
40146                                 }
40147                             }
40148                         
40149                         }
40150                     ]
40151                 }
40152                   
40153                 
40154             ]
40155         }
40156     ],
40157     buttons : [
40158         {
40159             xtype : 'Button',
40160             xns : 'Roo',
40161             text : "Forgot Password",
40162             listeners : {
40163                 click : function() {
40164                     //console.log(this);
40165                     var n = this.form.findField('username').getValue();
40166                     if (!n.length) {
40167                         Roo.MessageBox.alert("Error", "Fill in your email address");
40168                         return;
40169                     }
40170                     Roo.Ajax.request({
40171                         url: this.dialog.url,
40172                         params: {
40173                             passwordRequest: n
40174                         },
40175                         method: this.dialog.method,
40176                         success:  function(response, opts)  {  // check successfull...
40177                         
40178                             var res = this.dialog.processResponse(response);
40179                             if (!res.success) { // error!
40180                                Roo.MessageBox.alert("Error" ,
40181                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
40182                                return;
40183                             }
40184                             Roo.MessageBox.alert("Notice" ,
40185                                 "Please check you email for the Password Reset message");
40186                         },
40187                         failure : function() {
40188                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
40189                         }
40190                         
40191                     });
40192                 }
40193             }
40194         },
40195         {
40196             xtype : 'Button',
40197             xns : 'Roo',
40198             text : "Login",
40199             listeners : {
40200                 
40201                 click : function () {
40202                         
40203                     this.dialog.el.mask("Logging in");
40204                     this.form.doAction('submit', {
40205                             url: this.dialog.url,
40206                             method: this.dialog.method
40207                     });
40208                 }
40209             }
40210         }
40211     ]
40212   
40213   
40214 })
40215  
40216
40217
40218